library(nat)
library(neuprintr)
library(tidyverse)
library(ggraph)
library(plotly)
library(dplyr)
library(pbapply)
library(tidygraph)
library(gridExtra)
library(neuprintrExtra)
library(paletteer)
library(alphahull)

# Go to the neurpintR-notebooks directory before loading these functions
source("visualizeConnectivityTables.R")
# source("neuprintQueryUtils.R")
source("R/table2ggraphUtils.R")
source("R/connectivityMatricesTools.R")
# source("InputOutputByTypeUtils.R")
# source("R/rois.R")
# source("R/supertypeUtils.R")
# source("colorCodeLookup.R")
source("pathways.R")
source("inputOutputRegionsVis.R")
source("GetNeuronsInRoi.R")
source("FBNetworkVisUtils.R")
source("CX_ContextualAnalysisUtils.R")

options(nat.plotengine = 'rgl')

Configurable inputs: choose save/plot directories

# Directory to save data to.
SaveDir = "/Users/danc/Dropbox (HHMI)/WorkDrop/Data/FIBSEM_Analysis"

# Directory to save plots to.
PlotDir = "/Users/danc/Dropbox (HHMI)/WorkDrop/Data/FIBSEM_Analysis"

Create save/plot directory if they dont exists yet

if (!dir.exists(SaveDir)){dir.create(SaveDir)}
if (!dir.exists(PlotDir)){dir.create(PlotDir)}

Connect to neuprint

# neuprint_login()
neuprint_login(config=httr::set_config(httr::config(ssl_verifypeer = 0L)))

Creating a relatively high level ROI set

roisH <- getRoiTree()
roiSelection <- selectRoiSet(roisH,default_level=1,exceptions=list("CX"=2))

Pulling neurons postsynaptic to MBONs

MBON_Names <- neuprint_search("MBON.*")
# Find the postsynaptic partners of all MBONs
MBON_PostConnections <- getConnectionTable(MBON_Names,synapseType = "POST") ## Keeping the default threshold (3)

Get MBON targets inside / outside of CX

# Selecting CX types as those present in Romain's list.
CXTypes <- supertype(read_csv("/Users/danc/Dropbox (HHMI)/WorkDrop/Manuscripts/FIBSEM CX Paper Jan 2020/CX-cell-types060920") %>% rename(databaseType=n.type))
MBON_targets <- unique(MBON_PostConnections$type.to)

# Pick MBON targets in the CX
MBONCXTargetsL <- MBON_targets[MBON_targets %in% CXTypes$databaseType]
MBONCXTargets <- getTypesTable(MBONCXTargetsL)

# Pick MBON targets not in the CX
MBONnonCXTargetsL <- MBON_targets[!(MBON_targets %in% CXTypes$databaseType)]
MBONnonCXTargetsL <- na.omit(MBONnonCXTargetsL)
# Exclude MBONs from this list
MBONnonCXTargetsL <- MBONnonCXTargetsL[!(MBONnonCXTargetsL %in% MBON_Names$type)]
MBONnonCXTargets <- getTypesTable(MBONnonCXTargetsL)

Find indirect CX targets of MBONs

# Find the postsynaptic partners of MBON non-CX non-MBON targets
MBON_2ndaryPostConnections <- getConnectionTable(MBONnonCXTargets$bodyid,synapseType = "POST") ## Keeping the default threshold (3)

# Find the MBON secondary targets in the CX
MBONCX2ndaryTargetsL <- unique(MBON_2ndaryPostConnections$type.to)[unique(MBON_2ndaryPostConnections$type.to) %in% CXTypes$databaseType]
MBONCX2ndaryTargets <- getTypesTable(MBONCX2ndaryTargetsL)

Creating neuron bags of MBON primary and secondary CX targets

MBONTargetBag <- buildInputsOutputsByType(MBONCXTargets,slctROI=unique(roiSelection$roi))
MBONnonCXTargetBag <- buildInputsOutputsByType(MBONnonCXTargets,slctROI=unique(roiSelection$roi))
MBON2ndaryTargetBag <- buildInputsOutputsByType(MBONCX2ndaryTargets,slctROI=unique(roiSelection$roi))

Separating left and right neurons in there as some tangential input regions might be cut on the left.

MBONTargetBagLat <- lateralizeInputOutputList(MBONTargetBag)
MBONnonCXTargetBagLat <- lateralizeInputOutputList(MBONnonCXTargetBag)
MBON2ndaryTargetBagLat <- lateralizeInputOutputList(MBON2ndaryTargetBag)

Isolating the MBON primary and secondary inputs to right side neurons in there

MBONTargetInp <- MBONTargetBagLat$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to))
MBON2ndaryTargetInp <- MBON2ndaryTargetBagLat$inputs %>% filter((databaseType.from %in% MBONnonCXTargetsL) & grepl("_R",type.to))

Get MBON to CX_R indirect connection table through *_R

# Filter MBONnonCXTargetBagLat$input from MBONs to *_R
MBONnonCXTargetInp <- MBONnonCXTargetBagLat$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to))

# Filter MBONnonCXTargetBagLat$ouputs from MBONnonCXTargetInp$type.to to CX_R
MBONnonCXTargetR2CXRtab <- MBONnonCXTargetBagLat$outputs %>% filter(type.from %in% MBONnonCXTargetInp$type.to & databaseType.to %in% CXTypes$databaseType & grepl("_R",type.to))
# Filter MBONnonCXTargetInp by type.to with CX_R targets
MBON2nonCXTargetRtab <- MBONnonCXTargetInp %>% filter(type.to %in% MBONnonCXTargetR2CXRtab$type.from)

Combine all input regions into one (to get a “more fair” relative weight). Add a variable, totalMBContribution, that summarize how much of the total input the MBONs contribute to, and use it to sort the CX types.

MBONTargetInputCombined <- combineRois(MBONTargetBagLat,unique(MBONTargetInp$roi),newRoi="MBON outputs")
`summarise()` regrouping output by 'from', 'to', 'name.from', 'name.to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
`summarise()` regrouping output by 'from', 'to', 'name.from', 'name.to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal', 'outputContribution' (override with `.groups` argument)
`summarise()` regrouping output by 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal' (override with `.groups` argument)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal', 'outputContribution' (override with `.groups` argument)
`summarise()` regrouping output by 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal' (override with `.groups` argument)
MBONTargetInpC <- MBONTargetInputCombined$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to)) %>% 
  group_by(roi,type.to) %>% mutate(totalMBContribution = sum(weightRelative)) %>% 
                            ungroup() %>%
                                     arrange(desc(totalMBContribution))  %>%
                                     mutate(type.to = factor(type.to,levels=unique(type.to)))## Filter MBON to right CX neurons (some of the left ones can be cut)

plotConnMat_MBON_CX_Romain <- plotConnectivityMatrix(MBONTargetInpC,byGroup="type")
print(plotConnMat_MBON_CX_Romain)
ggsave("MBON_CX_ConnectionsByType_weightRelative_Romain.eps", plot=plotConnMat_MBON_CX_Romain, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)


inputProportions <- ggplot(MBONTargetInpC,aes(x=type.to)) + geom_col(aes(y=weightRelative,fill=type.from)) + scale_fill_manual(values=typesPalette(unique(MBONTargetInpC$type.from)),name="MBON types",guide=guide_legend(ncol=2)) + theme_classic()  + theme(axis.text.x = element_text(angle = 90)) + labs(y="Relative weight in input regions",x="Type")
print(inputProportions)
ggsave("CXfromMBON_InputProportionsByType_weightRelative_Romain.eps", plot=inputProportions, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Combine all input regions and add the totalMBContribution variable for MBONnonCXTargetBagLat. This is then used to sort the MBONs to non-CX non-MBON targets_R table.

# Combine rois based on MBON2nonCXTargetRtab$roi and add a new roi for MBON outputs
MBON2nonCXTargetInputCombined <- combineRois(MBONnonCXTargetBagLat,unique(MBON2nonCXTargetRtab$roi),newRoi="MBON outputs")
`summarise()` regrouping output by 'from', 'to', 'name.from', 'name.to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
`summarise()` regrouping output by 'from', 'to', 'name.from', 'name.to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal', 'outputContribution' (override with `.groups` argument)
`summarise()` regrouping output by 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal' (override with `.groups` argument)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal', 'outputContribution' (override with `.groups` argument)
`summarise()` regrouping output by 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'previous.type.to', 'previous.type.from', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3', 'roi', 'outputContributionTotal' (override with `.groups` argument)
# Sort MBON to non-CX non-MBON _R connections by totalMBContribution descending
MBON2nonCXTargetRInpCombSort <- MBON2nonCXTargetInputCombined$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to)) %>% 
  group_by(roi,type.to) %>% mutate(totalMBContribution = sum(weightRelative)) %>% ungroup() %>% arrange(desc(totalMBContribution))  %>%
  mutate(type.to = factor(type.to,levels=unique(type.to)))

# Filter MBON2nonCXTargetInputCombined$ouputs from MBON2nonCXTargetRInpCombSort$type.to to CX_R
MBONnonCXTargetR2CXRInpCombined <- MBON2nonCXTargetInputCombined$outputs %>% filter(type.from %in% MBON2nonCXTargetRInpCombSort$type.to & databaseType.to %in% CXTypes$databaseType & grepl("_R",type.to))
# Filter MBON2nonCXTargetRInpCombSort by type.to with CX_R targets
MBON2nonCXwCXRTgtInpCombSort <- MBON2nonCXTargetRInpCombSort %>% filter(type.to %in% MBONnonCXTargetR2CXRInpCombined$type.from)

plot_MBON2nonCXwCXRTgtInpCombSort <- plotConnectivityMatrix(MBON2nonCXwCXRTgtInpCombSort,byGroup="type")
print(plot_MBON2nonCXwCXRTgtInpCombSort)
ggsave("MBON2nonCXnonMBONTgtRwCXRTgt_InpCombinedSorted.eps", plot=plot_MBON2nonCXwCXRTgtInpCombSort, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)


inputProportions_MBON2nonCXwCXRTgtInpCombSort <- ggplot(MBON2nonCXwCXRTgtInpCombSort,aes(x=type.to)) + geom_col(aes(y=weightRelative,fill=type.from)) + scale_fill_manual(values=typesPalette(unique(MBON2nonCXwCXRTgtInpCombSort$type.from)),name="MBON types",guide=guide_legend(ncol=2)) + theme_classic()  + theme(axis.text.x = element_text(angle = 90)) + labs(y="Relative weight in input regions",x="Type")
47 levels in your palette, this is likely too many.
print(inputProportions_MBON2nonCXwCXRTgtInpCombSort)
ggsave("MBON2nonCXnonMBONTgtRwCXRTgt_InputProportionsCombinedSorted.eps", plot=inputProportions_MBON2nonCXwCXRTgtInpCombSort, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Plot the MBON to CX_R indrect pathways

# Pull significant MBON2nonCXwCXRTgtInpCombSort connections based on "totalMBContribution"
StrongMBON2nonCXwCXRTgtInpCombSort <- MBON2nonCXwCXRTgtInpCombSort %>% filter(totalMBContribution > 0.05)
# Filter MBONnonCXTargetR2CXRInpCombined by type.from among StrongMBON2nonCXwCXRTgtInpCombSort$type.to and weightRelative > 0.01
StrongMBONnonCXTargetR2CXRtab <- MBONnonCXTargetR2CXRInpCombined %>% filter(type.from %in% StrongMBON2nonCXwCXRTgtInpCombSort$type.to & weightRelative > 0.01)
# Filter StrongMBON2nonCXwCXRTgtInpCombSort for those connecting to StrongMBONnonCXTargetR2CXRtab$type.from
StrongMBON2nonCXwCXRTgtInpCombSort <- StrongMBON2nonCXwCXRTgtInpCombSort %>% filter(type.to %in% StrongMBONnonCXTargetR2CXRtab$type.from)

# Make a combined table
StrongMBON2nonCXwCXRTgtTable <- StrongMBON2nonCXwCXRTgtInpCombSort %>% select(type.from,type.to,weightRelative)
StrongMBONnonCXTgtR2CXRTable <- StrongMBONnonCXTargetR2CXRtab %>% select(type.from,type.to,weightRelative)
StrongMBON2nonCXR2CXRTgtComboTable <- bind_rows(StrongMBON2nonCXwCXRTgtTable,StrongMBONnonCXTgtR2CXRTable, .id = NULL)

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2nonCXwCXRTgtTable$type.from)), unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.from)),
                  unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.to))))
numTypes <- length(types)
numMBONs <- length(unique(as.vector(StrongMBON2nonCXwCXRTgtTable$type.from)))
numMidNodes <- length(unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.from)))
numCXtargets <- length(unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numMBONs), rep(0,times = numMidNodes), rep(1,times = numCXtargets)), 
                      y = c(seq(-1,1,length.out = numMBONs), seq(-1,1,length.out = numMidNodes), seq(-1,1,length.out = numCXtargets)))

# Graph the TypeToType ConnTable using the lookupTable
StrongMBON2nonCXR2CXRTgtComboPath <- graphConTab_old(StrongMBON2nonCXR2CXRTgtComboTable,xyLookup,FALSE,TRUE)
StrongMBON2nonCXR2CXRTgtComboPath <- StrongMBON2nonCXR2CXRTgtComboPath + scale_y_reverse()
print(StrongMBON2nonCXR2CXRTgtComboPath)
ggsave("StrongMBON2nonCXR2CXRTgtComboPath.svg", plot=StrongMBON2nonCXR2CXRTgtComboPath, device="svg", path=PlotDir, scale=1, 
       width=30, height=45, units="in", dpi=300, limitsize=FALSE)


# Cluster the tables by type.from and type.to
StrongMBON2nonCXwCXRTgt_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBON2nonCXwCXRTgtTable,'StrongMBON2nonCXwCXRTgt_Type2Type')

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths

Using weightRelative as value column: use value.var to override.
corrplot 0.84 loaded

StrongMBONnonCXTgtR2CXR_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBONnonCXTgtR2CXRTable,'StrongMBONnonCXTgtR2CXR_Type2Type')
Using weightRelative as value column: use value.var to override.

# Make and plot cosine distance matrix
StrongMBON2nonCXwCXRTgtInpCombSort_CosDist <- cosDistClusterPlot(PlotDir,StrongMBON2nonCXwCXRTgtInpCombSort,"StrongMBON2nonCXwCXRTgtInpCombSort")

StrongMBONnonCXTargetR2CXR_CosDist <- cosDistClusterPlot(PlotDir,StrongMBONnonCXTargetR2CXRtab,"StrongMBONnonCXTargetR2CXR")

Check if there’re cohorts of pathway clusters

Find Strongly connected direct MBON targets in the CX

# Pull significant MBONTargetInpC connections based on "totalMBContribution"
StrongMBONTargetInpC <- MBONTargetInpC  %>% filter(totalMBContribution > 0.05)
StrongMBON2CX_TargetsType <- unique(as.vector(StrongMBONTargetInpC$databaseType.to))
StrongMBON2CX_TargetNeurons <- getTypesTable(StrongMBON2CX_TargetsType)
# head(StrongMBON2CX_TargetNeurons)

# Find the MBON bodyids
StrongMBON2CX_FromType <- unique(as.vector(StrongMBONTargetInpC$databaseType.from))
StrongMBON2CX_FromNeurons <- getTypesTable(StrongMBON2CX_FromType)
# head(StrongMBON2CX_FromNeurons)

# Find neuron to neuron connection table for strong direct MBON to CX connections
StrongMBON2CX_ConnTable <- getConnectionTable_forSubset(StrongMBON2CX_FromNeurons$bodyid, StrongMBON2CX_TargetNeurons$bodyid)

# Arrange StrongMBON2CX_ConnTable in the order of StrongMBONTargetInpC$databaseType.to
StrongMBON2CX_ConnTable$type.to <- factor(StrongMBON2CX_ConnTable$type.to,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
StrongMBON2CX_ConnTable <- arrange(StrongMBON2CX_ConnTable,type.to)

# Plot StrongMBON2CX_ConnTable
plotStrongMBON2CX_ConnTable <- plotConnectivityMatrix(StrongMBON2CX_ConnTable,byGroup="id")
print(plotStrongMBON2CX_ConnTable)
ggsave("plotStrongMBON2CX_ConnTable.eps", plot=plotStrongMBON2CX_ConnTable, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Plot the strong direct pathways from MBONs to CX

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBONTargetInpC$type.from)),unique(as.vector(StrongMBONTargetInpC$type.to))))
numFrom <- length(unique(as.vector(StrongMBONTargetInpC$type.from)))
numTo <- length(unique(as.vector(StrongMBONTargetInpC$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTo)), y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTo)))

# Graph the TypeToType ConnTable using the lookupTable
StrongMBONTargetInpCdf <- StrongMBONTargetInpC
StrongMBONTargetInpCdf$type.to <- as.vector(StrongMBONTargetInpCdf$type.to)
gg_MBON2TargetTab <- graphConTab_old(StrongMBONTargetInpCdf,xyLookup,FALSE,TRUE)
# gg_MBON2TargetTab <- graphConTabPolyChrome(StrongMBONTargetInpCdf,xyLookup,FALSE,TRUE)
gg_MBON2TargetTab <- gg_MBON2TargetTab + scale_y_reverse()
print(gg_MBON2TargetTab)
ggsave("StrongMBON2CXtargetsTab.svg", plot=gg_MBON2TargetTab, device="svg", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Notes: 1) MBON05 has no branches ipsilateral to the cell body, only contralateral. FB4R only has dendrites ipsilateral to cell body. Therefore, only MBON05_L (not MBON05_R) makes synapses onto FB4R_R. 2) MBON09 has bilateral branches, but only makes contact with ipsilateral FB4R, i.e., MBON09_R onto FB4R_R. 3) MBON21 has bilateral branches, and synapses onto FB4R bilaterally. 4) MBON04 has bilateral branches, but more contralaterally relative to the name (although the *_L cell body is just off to the midline on the right side). 5) MBON12R and MBON13R only have branches ipsilateral to the cell body. 6) MBON03 has bilateral branches, but more contralaterally relative to the name. 7) MBON22 has ipsilateral branches in the calyx and bilateral branches in SIP/SMP. 8) MBON30 has bilateral branches, but more ipsilateral to the cell body.

Plot the combined direct and indirect pathways from MBONs to CX_R

# Combine MBON to CX_R direct and indirect tables 
StrongMBON2CXRTgtTable <- StrongMBONTargetInpCdf %>% select(type.from,type.to,weightRelative)
StrongMBON2CXRTgtDrctIndrctComboTable <- bind_rows(StrongMBON2CXRTgtTable,StrongMBON2nonCXR2CXRTgtComboTable, .id = NULL)

# Set up the layout for the combined pathway plot
MBONtypes <- unique(c(unique(as.vector(StrongMBON2CXRTgtTable$type.from)),unique(as.vector(StrongMBON2nonCXwCXRTgtTable$type.from))))
numMBONs <- length(MBONtypes)
midNodes <- unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.from))
numMidNodes <- length(midNodes)
allCXRtargets <- unique(c(unique(as.vector(StrongMBON2CXRTgtTable$type.to)),unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.to))))
numCXtargets <- length(allCXRtargets)
types <- unique(c(MBONtypes,midNodes,allCXRtargets))
numTypes <- length(types)

xyLookup = data.frame(type = types, x = c(rep(-1,times = numMBONs), rep(0,times = numMidNodes), rep(1,times = numCXtargets)), 
                      y = c(seq(-1,1,length.out = numMBONs), seq(1.1,3.1,length.out = numMidNodes), seq(-1,1.5,length.out = numCXtargets)))

# Graph the TypeToType ConnTable using the lookupTable
StrongMBON2CXRTgtDrctIndrctComboPath <- graphConTab_old(StrongMBON2CXRTgtDrctIndrctComboTable,xyLookup,FALSE,TRUE)
StrongMBON2CXRTgtDrctIndrctComboPath <- StrongMBON2CXRTgtDrctIndrctComboPath + scale_y_reverse()
print(StrongMBON2CXRTgtDrctIndrctComboPath)
ggsave("StrongMBON2CXRTgtDrctIndrctComboPath.svg", plot=StrongMBON2CXRTgtDrctIndrctComboPath, device="svg", path=PlotDir, scale=1, 
       width=30, height=90, units="in", dpi=300, limitsize=FALSE)

Find connections from strong direct MBON targets to secondary targets in CX

# Find the postsynaptic partners of StrongMBON2CX_TargetNeurons
StrongMBON2CX_TargetsCnctTable <- getConnectionTable(StrongMBON2CX_TargetNeurons,synapseType="POST")
# head(StrongMBON2CX_TargetsCnctTable)
StrongMBON2CXTargetsToPostInFB <- getConnectionTable_forSubset(StrongMBON2CX_TargetNeurons$bodyid, StrongMBON2CX_TargetsCnctTable$to,"FB")

# Arrange StrongMBON2CXTargetsToPostInFB in the order of StrongMBONTargetInpC$databaseType.to
StrongMBON2CXTargetsToPostInFB$type.from <- factor(StrongMBON2CXTargetsToPostInFB$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
StrongMBON2CXTargetsToPostInFB <- arrange(StrongMBON2CXTargetsToPostInFB,type.from)

# Plot and save the StrongMBON2CXTargetsToPostInFB based on the connectionMeasure of "weightRelative"
plotStrongMBON2CXTargetsToPostInFB <- plotConnectivityMatrix(StrongMBON2CXTargetsToPostInFB,byGroup="id")
print(plotStrongMBON2CXTargetsToPostInFB)
ggsave("plotStrongMBON2CXTargetsToPostInFB.eps", plot=plotStrongMBON2CXTargetsToPostInFB, device="eps", path=PlotDir, scale=1, 
       width=80, height=10, units="in", dpi=300, limitsize=FALSE)


# Get type to type connection table
StrongMBON2CXTargetsToPostInFB$type.from <- as.vector(StrongMBON2CXTargetsToPostInFB$type.from)
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- getTypeToTypeTable(StrongMBON2CXTargetsToPostInFB)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'roi', 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3' (override with `.groups` argument)
`summarise()` regrouping output by 'roi', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab %>% arrange(type.from %in% StrongMBON2CX_TargetsType)

# Filter the table by weightRelative
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab  %>% filter(weightRelative > 0.01)
# Exclude connections to self types
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab %>% filter(type.from != type.to)

# Arrange StrongMBON2CXTargetsToPostInFB_Type2TypeTab in the order of StrongMBONTargetInpC$databaseType.to
StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from <- factor(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- arrange(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,type.from)

# Plot and save the StrongMBON2CXTargetsToPostInFB_Type2TypeTab based on the connectionMeasure of "weightRelative"
plotStrongMBON2CXTargetsToPostInFB_Type2TypeConn <- plotConnectivityMatrix(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
print(plotStrongMBON2CXTargetsToPostInFB_Type2TypeConn)
ggsave("StrongMBON2CXTargetsToPostInFB_Type2TypeTable_weightRelative.eps", plot=plotStrongMBON2CXTargetsToPostInFB_Type2TypeConn, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Plot the pathways from strong direct MBON->CX targets to secondary CX targets

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from)),sort(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.to)))  )   )
numTypes <- length(types)
numFrom <- length(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from)))
numTo <- length(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.to)))

# Arrange the primary MBON->CX targets in a circle
angsFrom <- seq(-pi,pi,length.out = numFrom + 1)
angsFrom <- angsFrom[1:(length(angsFrom)-1)]
xFrom <- 1*sin(angsFrom)
yFrom <- 1*cos(angsFrom)

# Arrange the secondary MBON->CX targets in a bigger circle
angsTo <- seq(-pi,pi,length.out = numTypes - numFrom + 1)
angsTo <- angsTo[1:(length(angsTo)-1)]
xTo <-3*sin(angsTo)
yTo <-3*cos(angsTo)

# xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTypes-numFrom)), 
#                      y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTypes-numFrom)))

# xyLookup = data.frame(type = types, x = c(xFrom,rep(3,times = numTypes-numFrom)), 
#                      y = c(yFrom, seq(-8,8,length.out = numTypes-numFrom)))

xyLookup = data.frame(type = types, x = c(xFrom,xTo), y = c(yFrom,yTo))

# Graph the TypeToType ConnTable using the xyLookup lookupTable
StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from <- as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from)
gg_MBON1stTo2ndaryTargetTab <- graphConTab_old(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,xyLookup,FALSE,TRUE)
gg_MBON1stTo2ndaryTargetTab <- gg_MBON1stTo2ndaryTargetTab + scale_y_reverse()
print(gg_MBON1stTo2ndaryTargetTab)
ggsave("MBON1stTo2ndaryTargetTab.svg", plot=gg_MBON1stTo2ndaryTargetTab, device="svg", path=PlotDir, scale=1, 
       width=16, height=16, units="in", dpi=300, limitsize=FALSE)

Cluster MB->CX primary targets by the correlation of their weightRelative connectivity to secondary targets and the inverse (clustering secondary targets based on their inputs)

StrongMBON2CXTargetsToPostInFB_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBON2CXTargetsToPostInFB_Type2TypeTab,'StrongMBON2CXTargetsToPostInFB_Type2Type')
Using weightRelative as value column: use value.var to override.

StrongMBON2CXTargetsToPostInFB_Type2TypeTab_hcByFrom <- StrongMBON2CXTargetsToPostInFB_Type2Type_clustered[[1]]
StrongMBON2CXTargetsToPostInFB_Type2TypeTab_hcByTo <- StrongMBON2CXTargetsToPostInFB_Type2Type_clustered[[2]]
rm(StrongMBON2CXTargetsToPostInFB_Type2Type_clustered);
# Plot the connections by pathwayWeight (= totalMBContribution * weightRelative)

Find tertiary MBON targets in the CX

# Find the postsynaptic partners of secondary StrongMBON2CX_TargetNeurons
StrongMBON2CX_2ndTargetsInFB_Types <- unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$databaseType.to))
StrongMBON2CX_2ndTargetsInFB_Neurons <- getTypesTable(StrongMBON2CX_2ndTargetsInFB_Types)
StrongMBON2CX_2ndTargetsCnctTable <- getConnectionTable(StrongMBON2CX_2ndTargetsInFB_Neurons$bodyid,synapseType="POST")
# head(StrongMBON2CX_2ndTargetsCnctTable)
StrongMBON2CX_2ndTargetsToPostInFB <- getConnectionTable_forSubset(StrongMBON2CX_2ndTargetsInFB_Neurons$bodyid, StrongMBON2CX_2ndTargetsCnctTable$to,"FB")

# Arrange StrongMBON2CXTargetsToPostInFB in the order of StrongMBONTargetInpC$databaseType.to
# StrongMBON2CXTargetsToPostInFB$type.from <- factor(StrongMBON2CXTargetsToPostInFB$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
# StrongMBON2CXTargetsToPostInFB <- arrange(StrongMBON2CXTargetsToPostInFB,type.from)

# Plot and save the StrongMBON2CX_2ndTargetsToPostInFB based on the connectionMeasure of "weightRelative"
plotStrongMBON2CX2ndTargetsToPostInFB <- plotConnectivityMatrix(StrongMBON2CX_2ndTargetsToPostInFB,byGroup="id")
print(plotStrongMBON2CX2ndTargetsToPostInFB)
ggsave("plotStrongMBON2CX2ndTargetsToPostInFB.eps", plot=plotStrongMBON2CX2ndTargetsToPostInFB, device="eps", path=PlotDir, scale=1, 
       width=180, height=80, units="in", dpi=300, limitsize=FALSE)


# Get type to type connection table
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab <- getTypeToTypeTable(StrongMBON2CX_2ndTargetsToPostInFB)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'roi', 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3' (override with `.groups` argument)
`summarise()` regrouping output by 'roi', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab %>% arrange(type.from %in% StrongMBON2CX_TargetsType)

# Filter the table by weightRelative
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab  %>% filter(weightRelative > 0.01)
# Exclude connections to self types
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab %>% filter(type.from != type.to)

# Arrange StrongMBON2CXTargetsToPostInFB_Type2TypeTab in the order of StrongMBONTargetInpC$databaseType.to
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from <- factor(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- arrange(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,type.from)

# Plot and save the StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab based on the connectionMeasure of "weightRelative"
plotStrongMBON2CX2ndTargetsToPostInFB_Type2Type <- plotConnectivityMatrix(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
print(plotStrongMBON2CX2ndTargetsToPostInFB_Type2Type)
ggsave("StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTable_weightRelative.eps", plot=plotStrongMBON2CX2ndTargetsToPostInFB_Type2Type, device="eps", path=PlotDir, scale=1, 
       width=24, height=16, units="in", dpi=300, limitsize=FALSE)

Plot the pathways from strong direct MBON->CX secondary CX targets to tertiary CX targets

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.from)),sort(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.to)))  )   )
numTypes <- length(types)
numFrom <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.from)))
numTo <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.to)))

# Arrange the secondary MBON->CX targets in a circle
angsFrom <- seq(-pi,pi,length.out = numFrom + 1)
angsFrom <- angsFrom[1:(length(angsFrom)-1)]
xFrom <- 4*sin(angsFrom)
yFrom <- 4*cos(angsFrom)

# Arrange the tertiary MBON->CX targets in a bigger circle
angsTo <- seq(-pi,pi,length.out = numTypes - numFrom + 1)
angsTo <- angsTo[1:(length(angsTo)-1)]
xTo <-5*sin(angsTo)
yTo <-5*cos(angsTo)

# xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTypes-numFrom)), 
#                      y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTypes-numFrom)))

# xyLookup = data.frame(type = types, x = c(xFrom,rep(3,times = numTypes-numFrom)), 
#                      y = c(yFrom, seq(-8,8,length.out = numTypes-numFrom)))

xyLookup = data.frame(type = types, x = c(xFrom,xTo), y = c(yFrom,yTo))

# Graph the TypeToType ConnTable using the xyLookup lookupTable
gg_MBON2ndTo3rdTargetTab <- graphConTab_old(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,xyLookup,FALSE,TRUE)
gg_MBON2ndTo3rdTargetTab <- gg_MBON2ndTo3rdTargetTab + scale_y_reverse()
print(gg_MBON2ndTo3rdTargetTab)
ggsave("MBON2ndTo3rdCXTargetTab.svg", plot=gg_MBON2ndTo3rdTargetTab, device="svg", path=PlotDir, scale=1, 
       width=30, height=30, units="in", dpi=300, limitsize=FALSE)

Cluster MB->CX secondary targets by the correlation of their weightRelative connectivity to tertiary targets and the inverse (clustering tertiary targets based on their inputs)

StrongMBON2CX_2ndTargetsToPostInFB_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,'StrongMBON2CX_2ndTargetsToPostInFB_Type2Type')
Using weightRelative as value column: use value.var to override.

StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo <- StrongMBON2CX_2ndTargetsToPostInFB_Type2Type_clustered[[2]]

Pathway plot based on the clustered and rearranged StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.from)),unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.to))))
numTypes <- length(types)
numFrom <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.from)))
numTo <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTypes-numFrom)), y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTypes-numFrom)))

# Graph the TypeToType ConnTable using the lookupTable
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy$type.to <- as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy$type.to)
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway <- graphConTab_old(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy,xyLookup,FALSE,TRUE)
# gg_MBON2TargetTab <- graphConTabPolyChrome(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway,xyLookup,FALSE,TRUE)
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway + scale_y_reverse()
print(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway)
ggsave("StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_clusterByTo_pathway.svg", plot=StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway, device="svg", path=PlotDir, scale=1, 
       width=30, height=60, units="in", dpi=300, limitsize=FALSE)

ran well up to here

put a break here

# Run t-SNE on the type.to of StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab
# library(M3C)
# tsne(Data4Clust,perplex=15)

Check indirect connections from MBONs to the CX

# Plot neuron to neuron connection matrix from MBONs to non-CX non-MBON targets
MBON_nonCXnonMBON_ConnTable <- MBON_PostConnections %>% filter(type.to %in% MBONnonCXTargetsL)
plotMBON_nonCXnonMBON_ConnTable <- plotConnectivityMatrix(MBON_nonCXnonMBON_ConnTable,byGroup="id",connectionMeasure="weightRelative")
print(plotMBON_nonCXnonMBON_ConnTable)


# Plot type to type connection matrix from MBONs to non-CX non-MBON targets
MBON_nonCXnonMBON_Type2TypeTab <- getTypeToTypeTable(MBON_nonCXnonMBON_ConnTable)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'roi', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3' (override with `.groups` argument)
`summarise()` regrouping output by 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'roi', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
# Filter the table by weightRelative
MBON_nonCXnonMBON_Type2TypeTab <- MBON_nonCXnonMBON_Type2TypeTab  %>% filter(weightRelative > 0.01)

plotMBON_nonCXnonMBON_Type2TypeConn <- plotConnectivityMatrix(MBON_nonCXnonMBON_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
print(plotMBON_nonCXnonMBON_Type2TypeConn)


# Plot neuron to neuron connection matrix from MBON non-CX non-MBON targets to their targets in the CX
MBON_nonCXnonMBON_2ndaryCX_ConnTable <- MBON_2ndaryPostConnections %>% filter(type.to %in% MBONCX2ndaryTargetsL)
plotMBON_nonCXnonMBON_2ndaryCX_ConnTable <- plotConnectivityMatrix(MBON_nonCXnonMBON_2ndaryCX_ConnTable,byGroup="id",connectionMeasure="weightRelative")
print(plotMBON_nonCXnonMBON_2ndaryCX_ConnTable)


# Plot type to type connection matrix from MBON non-CX non-MBON targets to their targets in the CX
MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- getTypeToTypeTable(MBON_nonCXnonMBON_2ndaryCX_ConnTable)
`summarise()` ungrouping output (override with `.groups` argument)
`summarise()` regrouping output by 'to', 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'roi', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3', 'supertype.from3' (override with `.groups` argument)
`summarise()` regrouping output by 'type.from', 'type.to', 'databaseType.to', 'databaseType.from', 'outputContributionTotal', 'previous.type.to', 'previous.type.from', 'roi', 'outputContribution', 'supertype.to1', 'supertype.from1', 'supertype.to2', 'supertype.from2', 'supertype.to3' (override with `.groups` argument)
# Filter the table by weightRelative
MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab  %>% filter(weightRelative > 0.01)
# Exclude connections to self types
MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab %>% filter(type.from != type.to)

plotMBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- plotConnectivityMatrix(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
print(plotMBON_nonCXnonMBON_2ndaryCX_Type2TypeTab)

# Filter MBON_nonCXnonMBON_Type2TypeTab based on type.to with direct connections in the CX
MBON_nonCXnonMBONwCX2ndT_Type2TypeTab <- MBON_nonCXnonMBON_Type2TypeTab %>% filter(type.to %in% unique(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.from))
MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab %>% filter(type.from %in% unique(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab$type.to))

# Plot pathways from MBONs to non-CX non-MBON targets to secondary CX targets
# Combine tables
MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboTab <- bind_rows(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab,MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab, .id = NULL)

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab$type.from)), unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.from)),
                  unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.to))))
numTypes <- length(types)
numMBONs <- length(unique(as.vector(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab$type.from)))
numMidNodes <- length(unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.from)))
numCXtargets <- length(unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numMBONs), rep(0,times = numMidNodes), rep(1,times = numCXtargets)), 
                      y = c(seq(-1,1,length.out = numMBONs), seq(-1,1,length.out = numMidNodes), seq(-1,1,length.out = numCXtargets)))

# Graph the TypeToType ConnTable using the lookupTable
MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath <- graphConTab_old(MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboTab,xyLookup,FALSE,TRUE)
MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath + scale_y_reverse()
# print(MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath)
ggsave("MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath.svg", plot=MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath, device="svg", path=PlotDir, scale=1, 
       width=30, height=45, units="in", dpi=300, limitsize=FALSE)

Combine direct and indirect MBON->CX connection tables

MBON2ndaryCXTargetTypes <- unique(as.vector(MBON2ndaryTargetInp$databaseType.to))

Focus on the FB now (the only exception is LCNp which can probably be treated separately) and try to compute a “pathway weight” per CX target type. For every connection, MBONInfluence is the product of the relative weight with the total contribution of MBONs to the presynaptic neuron. pathwaysWeight is the sum of that over a target type (basically the sum of all its indirect MBON influences).

***Should also find pathways through middle neurons outside of the CX.

MBONTargetFBOut <- MBONTargetBagLat$outputs %>% filter(roi=="FB" & type.from %in% MBONTargetInpC$type.to) %>% 
  mutate(MBONInfluence = MBONTargetInpC$totalMBContribution[match(type.from,MBONTargetInpC$type.to)]*weightRelative) %>% group_by(type.to) %>% mutate(pathwaysWeight = sum(MBONInfluence)) %>% ungroup()
  

Looking at this table and sorting it according to various ways, the things that first pops out is that it’s mostly a FB tangential network (meaning most FB tangential neuron targets in the FB are other FB tangential neurons, I guess something reminiscent of the ring neurons). So we’d need a way to wean this recurrence in the way we consider which parts of the network are influenced by the MBONs.

MBONTargetFBOutSummary <- MBONTargetFBOut %>% group_by(type.to) %>% summarise(pathwaysWeight = pathwaysWeight[1])

Considering FB4R separately

MBONTargetFB4R <- MBONTargetFBOut %>% filter(type.from == "FB4R_R") %>% group_by(type.to) %>% summarise(pathwaysWeight = pathwaysWeight[1])

Suggestion: redo that, but study the pool of neurons influenced by a single MBON type at a time.

Other axis to explore: FB tangential inhomogeneous inputs onto columnar neurons.

LS0tCnRpdGxlOiAiTUJPTiBSZWR1eCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShuYXQpCmxpYnJhcnkobmV1cHJpbnRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBiYXBwbHkpCmxpYnJhcnkodGlkeWdyYXBoKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShuZXVwcmludHJFeHRyYSkKbGlicmFyeShwYWxldHRlZXIpCmxpYnJhcnkoYWxwaGFodWxsKQoKIyBHbyB0byB0aGUgbmV1cnBpbnRSLW5vdGVib29rcyBkaXJlY3RvcnkgYmVmb3JlIGxvYWRpbmcgdGhlc2UgZnVuY3Rpb25zCnNvdXJjZSgidmlzdWFsaXplQ29ubmVjdGl2aXR5VGFibGVzLlIiKQojIHNvdXJjZSgibmV1cHJpbnRRdWVyeVV0aWxzLlIiKQpzb3VyY2UoIlIvdGFibGUyZ2dyYXBoVXRpbHMuUiIpCnNvdXJjZSgiUi9jb25uZWN0aXZpdHlNYXRyaWNlc1Rvb2xzLlIiKQojIHNvdXJjZSgiSW5wdXRPdXRwdXRCeVR5cGVVdGlscy5SIikKIyBzb3VyY2UoIlIvcm9pcy5SIikKIyBzb3VyY2UoIlIvc3VwZXJ0eXBlVXRpbHMuUiIpCiMgc291cmNlKCJjb2xvckNvZGVMb29rdXAuUiIpCnNvdXJjZSgicGF0aHdheXMuUiIpCnNvdXJjZSgiaW5wdXRPdXRwdXRSZWdpb25zVmlzLlIiKQpzb3VyY2UoIkdldE5ldXJvbnNJblJvaS5SIikKc291cmNlKCJGQk5ldHdvcmtWaXNVdGlscy5SIikKc291cmNlKCJDWF9Db250ZXh0dWFsQW5hbHlzaXNVdGlscy5SIikKCm9wdGlvbnMobmF0LnBsb3RlbmdpbmUgPSAncmdsJykKYGBgCgojIyMgQ29uZmlndXJhYmxlIGlucHV0czogY2hvb3NlIHNhdmUvcGxvdCBkaXJlY3RvcmllcwpgYGB7cn0KIyBEaXJlY3RvcnkgdG8gc2F2ZSBkYXRhIHRvLgpTYXZlRGlyID0gIi9Vc2Vycy9kYW5jL0Ryb3Bib3ggKEhITUkpL1dvcmtEcm9wL0RhdGEvRklCU0VNX0FuYWx5c2lzIgoKIyBEaXJlY3RvcnkgdG8gc2F2ZSBwbG90cyB0by4KUGxvdERpciA9ICIvVXNlcnMvZGFuYy9Ecm9wYm94IChISE1JKS9Xb3JrRHJvcC9EYXRhL0ZJQlNFTV9BbmFseXNpcyIKYGBgCgojIyMgQ3JlYXRlIHNhdmUvcGxvdCBkaXJlY3RvcnkgaWYgdGhleSBkb250IGV4aXN0cyB5ZXQKYGBge3J9CmlmICghZGlyLmV4aXN0cyhTYXZlRGlyKSl7ZGlyLmNyZWF0ZShTYXZlRGlyKX0KaWYgKCFkaXIuZXhpc3RzKFBsb3REaXIpKXtkaXIuY3JlYXRlKFBsb3REaXIpfQpgYGAKCiMjIyBDb25uZWN0IHRvIG5ldXByaW50CmBgYHtyfQojIG5ldXByaW50X2xvZ2luKCkKbmV1cHJpbnRfbG9naW4oY29uZmlnPWh0dHI6OnNldF9jb25maWcoaHR0cjo6Y29uZmlnKHNzbF92ZXJpZnlwZWVyID0gMEwpKSkKYGBgCgojIyMgQ3JlYXRpbmcgYSByZWxhdGl2ZWx5IGhpZ2ggbGV2ZWwgUk9JIHNldApgYGB7cn0Kcm9pc0ggPC0gZ2V0Um9pVHJlZSgpCnJvaVNlbGVjdGlvbiA8LSBzZWxlY3RSb2lTZXQocm9pc0gsZGVmYXVsdF9sZXZlbD0xLGV4Y2VwdGlvbnM9bGlzdCgiQ1giPTIpKQpgYGAKCiMjIyBQdWxsaW5nIG5ldXJvbnMgcG9zdHN5bmFwdGljIHRvIE1CT05zCmBgYHtyfQpNQk9OX05hbWVzIDwtIG5ldXByaW50X3NlYXJjaCgiTUJPTi4qIikKIyBGaW5kIHRoZSBwb3N0c3luYXB0aWMgcGFydG5lcnMgb2YgYWxsIE1CT05zCk1CT05fUG9zdENvbm5lY3Rpb25zIDwtIGdldENvbm5lY3Rpb25UYWJsZShNQk9OX05hbWVzLHN5bmFwc2VUeXBlID0gIlBPU1QiKSAjIyBLZWVwaW5nIHRoZSBkZWZhdWx0IHRocmVzaG9sZCAoMykKYGBgCgojIyMgR2V0IE1CT04gdGFyZ2V0cyBpbnNpZGUgLyBvdXRzaWRlIG9mIENYCmBgYHtyfQojIFNlbGVjdGluZyBDWCB0eXBlcyBhcyB0aG9zZSBwcmVzZW50IGluIFJvbWFpbidzIGxpc3QuCkNYVHlwZXMgPC0gc3VwZXJ0eXBlKHJlYWRfY3N2KCIvVXNlcnMvZGFuYy9Ecm9wYm94IChISE1JKS9Xb3JrRHJvcC9NYW51c2NyaXB0cy9GSUJTRU0gQ1ggUGFwZXIgSmFuIDIwMjAvQ1gtY2VsbC10eXBlczA2MDkyMCIpICU+JSByZW5hbWUoZGF0YWJhc2VUeXBlPW4udHlwZSkpCk1CT05fdGFyZ2V0cyA8LSB1bmlxdWUoTUJPTl9Qb3N0Q29ubmVjdGlvbnMkdHlwZS50bykKCiMgUGljayBNQk9OIHRhcmdldHMgaW4gdGhlIENYCk1CT05DWFRhcmdldHNMIDwtIE1CT05fdGFyZ2V0c1tNQk9OX3RhcmdldHMgJWluJSBDWFR5cGVzJGRhdGFiYXNlVHlwZV0KTUJPTkNYVGFyZ2V0cyA8LSBnZXRUeXBlc1RhYmxlKE1CT05DWFRhcmdldHNMKQoKIyBQaWNrIE1CT04gdGFyZ2V0cyBub3QgaW4gdGhlIENYCk1CT05ub25DWFRhcmdldHNMIDwtIE1CT05fdGFyZ2V0c1shKE1CT05fdGFyZ2V0cyAlaW4lIENYVHlwZXMkZGF0YWJhc2VUeXBlKV0KTUJPTm5vbkNYVGFyZ2V0c0wgPC0gbmEub21pdChNQk9Obm9uQ1hUYXJnZXRzTCkKIyBFeGNsdWRlIE1CT05zIGZyb20gdGhpcyBsaXN0Ck1CT05ub25DWFRhcmdldHNMIDwtIE1CT05ub25DWFRhcmdldHNMWyEoTUJPTm5vbkNYVGFyZ2V0c0wgJWluJSBNQk9OX05hbWVzJHR5cGUpXQpNQk9Obm9uQ1hUYXJnZXRzIDwtIGdldFR5cGVzVGFibGUoTUJPTm5vbkNYVGFyZ2V0c0wpCmBgYAoKIyMjIEZpbmQgaW5kaXJlY3QgQ1ggdGFyZ2V0cyBvZiBNQk9OcwpgYGB7cn0KIyBGaW5kIHRoZSBwb3N0c3luYXB0aWMgcGFydG5lcnMgb2YgTUJPTiBub24tQ1ggbm9uLU1CT04gdGFyZ2V0cwpNQk9OXzJuZGFyeVBvc3RDb25uZWN0aW9ucyA8LSBnZXRDb25uZWN0aW9uVGFibGUoTUJPTm5vbkNYVGFyZ2V0cyRib2R5aWQsc3luYXBzZVR5cGUgPSAiUE9TVCIpICMjIEtlZXBpbmcgdGhlIGRlZmF1bHQgdGhyZXNob2xkICgzKQoKIyBGaW5kIHRoZSBNQk9OIHNlY29uZGFyeSB0YXJnZXRzIGluIHRoZSBDWApNQk9OQ1gybmRhcnlUYXJnZXRzTCA8LSB1bmlxdWUoTUJPTl8ybmRhcnlQb3N0Q29ubmVjdGlvbnMkdHlwZS50bylbdW5pcXVlKE1CT05fMm5kYXJ5UG9zdENvbm5lY3Rpb25zJHR5cGUudG8pICVpbiUgQ1hUeXBlcyRkYXRhYmFzZVR5cGVdCk1CT05DWDJuZGFyeVRhcmdldHMgPC0gZ2V0VHlwZXNUYWJsZShNQk9OQ1gybmRhcnlUYXJnZXRzTCkKYGBgCgojIyMgQ3JlYXRpbmcgbmV1cm9uIGJhZ3Mgb2YgTUJPTiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgQ1ggdGFyZ2V0cwpgYGB7cn0KTUJPTlRhcmdldEJhZyA8LSBidWlsZElucHV0c091dHB1dHNCeVR5cGUoTUJPTkNYVGFyZ2V0cyxzbGN0Uk9JPXVuaXF1ZShyb2lTZWxlY3Rpb24kcm9pKSkKTUJPTm5vbkNYVGFyZ2V0QmFnIDwtIGJ1aWxkSW5wdXRzT3V0cHV0c0J5VHlwZShNQk9Obm9uQ1hUYXJnZXRzLHNsY3RST0k9dW5pcXVlKHJvaVNlbGVjdGlvbiRyb2kpKQpNQk9OMm5kYXJ5VGFyZ2V0QmFnIDwtIGJ1aWxkSW5wdXRzT3V0cHV0c0J5VHlwZShNQk9OQ1gybmRhcnlUYXJnZXRzLHNsY3RST0k9dW5pcXVlKHJvaVNlbGVjdGlvbiRyb2kpKQpgYGAKCiMjIyBTZXBhcmF0aW5nIGxlZnQgYW5kIHJpZ2h0IG5ldXJvbnMgaW4gdGhlcmUgYXMgc29tZSB0YW5nZW50aWFsIGlucHV0IHJlZ2lvbnMgbWlnaHQgYmUgY3V0IG9uIHRoZSBsZWZ0LgpgYGB7cn0KTUJPTlRhcmdldEJhZ0xhdCA8LSBsYXRlcmFsaXplSW5wdXRPdXRwdXRMaXN0KE1CT05UYXJnZXRCYWcpCk1CT05ub25DWFRhcmdldEJhZ0xhdCA8LSBsYXRlcmFsaXplSW5wdXRPdXRwdXRMaXN0KE1CT05ub25DWFRhcmdldEJhZykKTUJPTjJuZGFyeVRhcmdldEJhZ0xhdCA8LSBsYXRlcmFsaXplSW5wdXRPdXRwdXRMaXN0KE1CT04ybmRhcnlUYXJnZXRCYWcpCmBgYAoKIyMjIElzb2xhdGluZyB0aGUgTUJPTiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgaW5wdXRzIHRvIHJpZ2h0IHNpZGUgbmV1cm9ucyBpbiB0aGVyZQpgYGB7cn0KTUJPTlRhcmdldElucCA8LSBNQk9OVGFyZ2V0QmFnTGF0JGlucHV0cyAlPiUgZmlsdGVyKHN0YXJ0c1dpdGgodHlwZS5mcm9tLCJNQk9OIikgJiBncmVwbCgiX1IiLHR5cGUudG8pKQpNQk9OMm5kYXJ5VGFyZ2V0SW5wIDwtIE1CT04ybmRhcnlUYXJnZXRCYWdMYXQkaW5wdXRzICU+JSBmaWx0ZXIoKGRhdGFiYXNlVHlwZS5mcm9tICVpbiUgTUJPTm5vbkNYVGFyZ2V0c0wpICYgZ3JlcGwoIl9SIix0eXBlLnRvKSkKYGBgCgojIyMgR2V0IE1CT04gdG8gQ1hfUiBpbmRpcmVjdCBjb25uZWN0aW9uIHRhYmxlIHRocm91Z2ggKl9SCmBgYHtyfQojIEZpbHRlciBNQk9Obm9uQ1hUYXJnZXRCYWdMYXQkaW5wdXQgZnJvbSBNQk9OcyB0byAqX1IKTUJPTm5vbkNYVGFyZ2V0SW5wIDwtIE1CT05ub25DWFRhcmdldEJhZ0xhdCRpbnB1dHMgJT4lIGZpbHRlcihzdGFydHNXaXRoKHR5cGUuZnJvbSwiTUJPTiIpICYgZ3JlcGwoIl9SIix0eXBlLnRvKSkKCiMgRmlsdGVyIE1CT05ub25DWFRhcmdldEJhZ0xhdCRvdXB1dHMgZnJvbSBNQk9Obm9uQ1hUYXJnZXRJbnAkdHlwZS50byB0byBDWF9SCk1CT05ub25DWFRhcmdldFIyQ1hSdGFiIDwtIE1CT05ub25DWFRhcmdldEJhZ0xhdCRvdXRwdXRzICU+JSBmaWx0ZXIodHlwZS5mcm9tICVpbiUgTUJPTm5vbkNYVGFyZ2V0SW5wJHR5cGUudG8gJiBkYXRhYmFzZVR5cGUudG8gJWluJSBDWFR5cGVzJGRhdGFiYXNlVHlwZSAmIGdyZXBsKCJfUiIsdHlwZS50bykpCiMgRmlsdGVyIE1CT05ub25DWFRhcmdldElucCBieSB0eXBlLnRvIHdpdGggQ1hfUiB0YXJnZXRzCk1CT04ybm9uQ1hUYXJnZXRSdGFiIDwtIE1CT05ub25DWFRhcmdldElucCAlPiUgZmlsdGVyKHR5cGUudG8gJWluJSBNQk9Obm9uQ1hUYXJnZXRSMkNYUnRhYiR0eXBlLmZyb20pCmBgYAoKIyMjIENvbWJpbmUgYWxsIGlucHV0IHJlZ2lvbnMgaW50byBvbmUgKHRvIGdldCBhICJtb3JlIGZhaXIiIHJlbGF0aXZlIHdlaWdodCkuIEFkZCBhIHZhcmlhYmxlLCB0b3RhbE1CQ29udHJpYnV0aW9uLCB0aGF0IHN1bW1hcml6ZSBob3cgbXVjaCBvZiB0aGUgdG90YWwgaW5wdXQgdGhlIE1CT05zIGNvbnRyaWJ1dGUgdG8sIGFuZCB1c2UgaXQgdG8gc29ydCB0aGUgQ1ggdHlwZXMuCmBgYHtyfQpNQk9OVGFyZ2V0SW5wdXRDb21iaW5lZCA8LSBjb21iaW5lUm9pcyhNQk9OVGFyZ2V0QmFnTGF0LHVuaXF1ZShNQk9OVGFyZ2V0SW5wJHJvaSksbmV3Um9pPSJNQk9OIG91dHB1dHMiKQpNQk9OVGFyZ2V0SW5wQyA8LSBNQk9OVGFyZ2V0SW5wdXRDb21iaW5lZCRpbnB1dHMgJT4lIGZpbHRlcihzdGFydHNXaXRoKHR5cGUuZnJvbSwiTUJPTiIpICYgZ3JlcGwoIl9SIix0eXBlLnRvKSkgJT4lIAogIGdyb3VwX2J5KHJvaSx0eXBlLnRvKSAlPiUgbXV0YXRlKHRvdGFsTUJDb250cmlidXRpb24gPSBzdW0od2VpZ2h0UmVsYXRpdmUpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnJhbmdlKGRlc2ModG90YWxNQkNvbnRyaWJ1dGlvbikpICAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZSh0eXBlLnRvID0gZmFjdG9yKHR5cGUudG8sbGV2ZWxzPXVuaXF1ZSh0eXBlLnRvKSkpIyMgRmlsdGVyIE1CT04gdG8gcmlnaHQgQ1ggbmV1cm9ucyAoc29tZSBvZiB0aGUgbGVmdCBvbmVzIGNhbiBiZSBjdXQpCgpwbG90Q29ubk1hdF9NQk9OX0NYX1JvbWFpbiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05UYXJnZXRJbnBDLGJ5R3JvdXA9InR5cGUiKQpwcmludChwbG90Q29ubk1hdF9NQk9OX0NYX1JvbWFpbikKZ2dzYXZlKCJNQk9OX0NYX0Nvbm5lY3Rpb25zQnlUeXBlX3dlaWdodFJlbGF0aXZlX1JvbWFpbi5lcHMiLCBwbG90PXBsb3RDb25uTWF0X01CT05fQ1hfUm9tYWluLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xMSwgaGVpZ2h0PTgsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1UUlVFKQoKaW5wdXRQcm9wb3J0aW9ucyA8LSBnZ3Bsb3QoTUJPTlRhcmdldElucEMsYWVzKHg9dHlwZS50bykpICsgZ2VvbV9jb2woYWVzKHk9d2VpZ2h0UmVsYXRpdmUsZmlsbD10eXBlLmZyb20pKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz10eXBlc1BhbGV0dGUodW5pcXVlKE1CT05UYXJnZXRJbnBDJHR5cGUuZnJvbSkpLG5hbWU9Ik1CT04gdHlwZXMiLGd1aWRlPWd1aWRlX2xlZ2VuZChuY29sPTIpKSArIHRoZW1lX2NsYXNzaWMoKSAgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKyBsYWJzKHk9IlJlbGF0aXZlIHdlaWdodCBpbiBpbnB1dCByZWdpb25zIix4PSJUeXBlIikKcHJpbnQoaW5wdXRQcm9wb3J0aW9ucykKZ2dzYXZlKCJDWGZyb21NQk9OX0lucHV0UHJvcG9ydGlvbnNCeVR5cGVfd2VpZ2h0UmVsYXRpdmVfUm9tYWluLmVwcyIsIHBsb3Q9aW5wdXRQcm9wb3J0aW9ucywgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTEsIGhlaWdodD04LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9VFJVRSkKYGBgCgojIyMgQ29tYmluZSBhbGwgaW5wdXQgcmVnaW9ucyBhbmQgYWRkIHRoZSB0b3RhbE1CQ29udHJpYnV0aW9uIHZhcmlhYmxlIGZvciBNQk9Obm9uQ1hUYXJnZXRCYWdMYXQuIFRoaXMgaXMgdGhlbiB1c2VkIHRvIHNvcnQgdGhlIE1CT05zIHRvIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzX1IgdGFibGUuIApgYGB7cn0KIyBDb21iaW5lIHJvaXMgYmFzZWQgb24gTUJPTjJub25DWFRhcmdldFJ0YWIkcm9pIGFuZCBhZGQgYSBuZXcgcm9pIGZvciBNQk9OIG91dHB1dHMKTUJPTjJub25DWFRhcmdldElucHV0Q29tYmluZWQgPC0gY29tYmluZVJvaXMoTUJPTm5vbkNYVGFyZ2V0QmFnTGF0LHVuaXF1ZShNQk9OMm5vbkNYVGFyZ2V0UnRhYiRyb2kpLG5ld1JvaT0iTUJPTiBvdXRwdXRzIikKIyBTb3J0IE1CT04gdG8gbm9uLUNYIG5vbi1NQk9OIF9SIGNvbm5lY3Rpb25zIGJ5IHRvdGFsTUJDb250cmlidXRpb24gZGVzY2VuZGluZwpNQk9OMm5vbkNYVGFyZ2V0UklucENvbWJTb3J0IDwtIE1CT04ybm9uQ1hUYXJnZXRJbnB1dENvbWJpbmVkJGlucHV0cyAlPiUgZmlsdGVyKHN0YXJ0c1dpdGgodHlwZS5mcm9tLCJNQk9OIikgJiBncmVwbCgiX1IiLHR5cGUudG8pKSAlPiUgCiAgZ3JvdXBfYnkocm9pLHR5cGUudG8pICU+JSBtdXRhdGUodG90YWxNQkNvbnRyaWJ1dGlvbiA9IHN1bSh3ZWlnaHRSZWxhdGl2ZSkpICU+JSB1bmdyb3VwKCkgJT4lIGFycmFuZ2UoZGVzYyh0b3RhbE1CQ29udHJpYnV0aW9uKSkgICU+JQogIG11dGF0ZSh0eXBlLnRvID0gZmFjdG9yKHR5cGUudG8sbGV2ZWxzPXVuaXF1ZSh0eXBlLnRvKSkpCgojIEZpbHRlciBNQk9OMm5vbkNYVGFyZ2V0SW5wdXRDb21iaW5lZCRvdXB1dHMgZnJvbSBNQk9OMm5vbkNYVGFyZ2V0UklucENvbWJTb3J0JHR5cGUudG8gdG8gQ1hfUgpNQk9Obm9uQ1hUYXJnZXRSMkNYUklucENvbWJpbmVkIDwtIE1CT04ybm9uQ1hUYXJnZXRJbnB1dENvbWJpbmVkJG91dHB1dHMgJT4lIGZpbHRlcih0eXBlLmZyb20gJWluJSBNQk9OMm5vbkNYVGFyZ2V0UklucENvbWJTb3J0JHR5cGUudG8gJiBkYXRhYmFzZVR5cGUudG8gJWluJSBDWFR5cGVzJGRhdGFiYXNlVHlwZSAmIGdyZXBsKCJfUiIsdHlwZS50bykpCiMgRmlsdGVyIE1CT04ybm9uQ1hUYXJnZXRSSW5wQ29tYlNvcnQgYnkgdHlwZS50byB3aXRoIENYX1IgdGFyZ2V0cwpNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0IDwtIE1CT04ybm9uQ1hUYXJnZXRSSW5wQ29tYlNvcnQgJT4lIGZpbHRlcih0eXBlLnRvICVpbiUgTUJPTm5vbkNYVGFyZ2V0UjJDWFJJbnBDb21iaW5lZCR0eXBlLmZyb20pCgpwbG90X01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQgPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0LGJ5R3JvdXA9InR5cGUiKQpwcmludChwbG90X01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQpCmdnc2F2ZSgiTUJPTjJub25DWG5vbk1CT05UZ3RSd0NYUlRndF9JbnBDb21iaW5lZFNvcnRlZC5lcHMiLCBwbG90PXBsb3RfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCwgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTEsIGhlaWdodD04LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9VFJVRSkKCmlucHV0UHJvcG9ydGlvbnNfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCA8LSBnZ3Bsb3QoTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCxhZXMoeD10eXBlLnRvKSkgKyBnZW9tX2NvbChhZXMoeT13ZWlnaHRSZWxhdGl2ZSxmaWxsPXR5cGUuZnJvbSkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXR5cGVzUGFsZXR0ZSh1bmlxdWUoTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCR0eXBlLmZyb20pKSxuYW1lPSJNQk9OIHR5cGVzIixndWlkZT1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKyB0aGVtZV9jbGFzc2ljKCkgICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsgbGFicyh5PSJSZWxhdGl2ZSB3ZWlnaHQgaW4gaW5wdXQgcmVnaW9ucyIseD0iVHlwZSIpCnByaW50KGlucHV0UHJvcG9ydGlvbnNfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCkKZ2dzYXZlKCJNQk9OMm5vbkNYbm9uTUJPTlRndFJ3Q1hSVGd0X0lucHV0UHJvcG9ydGlvbnNDb21iaW5lZFNvcnRlZC5lcHMiLCBwbG90PWlucHV0UHJvcG9ydGlvbnNfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCwgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTEsIGhlaWdodD04LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9VFJVRSkKYGBgCgojIyMgUGxvdCB0aGUgTUJPTiB0byBDWF9SIGluZHJlY3QgcGF0aHdheXMKYGBge3J9CiMgUHVsbCBzaWduaWZpY2FudCBNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0IGNvbm5lY3Rpb25zIGJhc2VkIG9uICJ0b3RhbE1CQ29udHJpYnV0aW9uIgpTdHJvbmdNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0IDwtIE1CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQgJT4lIGZpbHRlcih0b3RhbE1CQ29udHJpYnV0aW9uID4gMC4wNSkKIyBGaWx0ZXIgTUJPTm5vbkNYVGFyZ2V0UjJDWFJJbnBDb21iaW5lZCBieSB0eXBlLmZyb20gYW1vbmcgU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCR0eXBlLnRvIGFuZCB3ZWlnaHRSZWxhdGl2ZSA+IDAuMDEKU3Ryb25nTUJPTm5vbkNYVGFyZ2V0UjJDWFJ0YWIgPC0gTUJPTm5vbkNYVGFyZ2V0UjJDWFJJbnBDb21iaW5lZCAlPiUgZmlsdGVyKHR5cGUuZnJvbSAlaW4lIFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQkdHlwZS50byAmIHdlaWdodFJlbGF0aXZlID4gMC4wMSkKIyBGaWx0ZXIgU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCBmb3IgdGhvc2UgY29ubmVjdGluZyB0byBTdHJvbmdNQk9Obm9uQ1hUYXJnZXRSMkNYUnRhYiR0eXBlLmZyb20KU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCA8LSBTdHJvbmdNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0ICU+JSBmaWx0ZXIodHlwZS50byAlaW4lIFN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSdGFiJHR5cGUuZnJvbSkKCiMgTWFrZSBhIGNvbWJpbmVkIHRhYmxlClN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUgPC0gU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCAlPiUgc2VsZWN0KHR5cGUuZnJvbSx0eXBlLnRvLHdlaWdodFJlbGF0aXZlKQpTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUlRhYmxlIDwtIFN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSdGFiICU+JSBzZWxlY3QodHlwZS5mcm9tLHR5cGUudG8sd2VpZ2h0UmVsYXRpdmUpClN0cm9uZ01CT04ybm9uQ1hSMkNYUlRndENvbWJvVGFibGUgPC0gYmluZF9yb3dzKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUsU3Ryb25nTUJPTm5vbkNYVGd0UjJDWFJUYWJsZSwgLmlkID0gTlVMTCkKCiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBwYXRod2F5IHBsb3QKdHlwZXMgPC0gdW5pcXVlKGModW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMm5vbkNYd0NYUlRndFRhYmxlJHR5cGUuZnJvbSkpLCB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUkdHlwZS5mcm9tKSksCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTm5vbkNYVGd0UjJDWFJUYWJsZSR0eXBlLnRvKSkpKQpudW1UeXBlcyA8LSBsZW5ndGgodHlwZXMpCm51bU1CT05zIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUkdHlwZS5mcm9tKSkpCm51bU1pZE5vZGVzIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUkdHlwZS5mcm9tKSkpCm51bUNYdGFyZ2V0cyA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUlRhYmxlJHR5cGUudG8pKSkKeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtTUJPTnMpLCByZXAoMCx0aW1lcyA9IG51bU1pZE5vZGVzKSwgcmVwKDEsdGltZXMgPSBudW1DWHRhcmdldHMpKSwgCiAgICAgICAgICAgICAgICAgICAgICB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtTUJPTnMpLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtTWlkTm9kZXMpLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtQ1h0YXJnZXRzKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKU3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9QYXRoIDwtIGdyYXBoQ29uVGFiX29sZChTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1RhYmxlLHh5TG9va3VwLEZBTFNFLFRSVUUpClN0cm9uZ01CT04ybm9uQ1hSMkNYUlRndENvbWJvUGF0aCA8LSBTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1BhdGggKyBzY2FsZV95X3JldmVyc2UoKQpwcmludChTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1BhdGgpCmdnc2F2ZSgiU3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9QYXRoLnN2ZyIsIHBsb3Q9U3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9QYXRoLCBkZXZpY2U9InN2ZyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0zMCwgaGVpZ2h0PTQ1LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCgojIENsdXN0ZXIgdGhlIHRhYmxlcyBieSB0eXBlLmZyb20gYW5kIHR5cGUudG8KU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RfVHlwZTJUeXBlX2NsdXN0ZXJlZCA8LSBwbG90Q29yck1hdENsdXN0ZXIoUGxvdERpcixTdHJvbmdNQk9OMm5vbkNYd0NYUlRndFRhYmxlLCdTdHJvbmdNQk9OMm5vbkNYd0NYUlRndF9UeXBlMlR5cGUnKQpTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUl9UeXBlMlR5cGVfY2x1c3RlcmVkIDwtIHBsb3RDb3JyTWF0Q2x1c3RlcihQbG90RGlyLFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUsJ1N0cm9uZ01CT05ub25DWFRndFIyQ1hSX1R5cGUyVHlwZScpCgojIE1ha2UgYW5kIHBsb3QgY29zaW5lIGRpc3RhbmNlIG1hdHJpeApTdHJvbmdNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0X0Nvc0Rpc3QgPC0gY29zRGlzdENsdXN0ZXJQbG90KFBsb3REaXIsU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCwiU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCIpClN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSX0Nvc0Rpc3QgPC0gY29zRGlzdENsdXN0ZXJQbG90KFBsb3REaXIsU3Ryb25nTUJPTm5vbkNYVGFyZ2V0UjJDWFJ0YWIsIlN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSIikKYGBgCgojIyMgQ2hlY2sgaWYgdGhlcmUncmUgY29ob3J0cyBvZiBwYXRod2F5IGNsdXN0ZXJzCmBgYHtyfQoKYGBgCgojIyMgRmluZCBTdHJvbmdseSBjb25uZWN0ZWQgZGlyZWN0IE1CT04gdGFyZ2V0cyBpbiB0aGUgQ1gKYGBge3J9CiMgUHVsbCBzaWduaWZpY2FudCBNQk9OVGFyZ2V0SW5wQyBjb25uZWN0aW9ucyBiYXNlZCBvbiAidG90YWxNQkNvbnRyaWJ1dGlvbiIKU3Ryb25nTUJPTlRhcmdldElucEMgPC0gTUJPTlRhcmdldElucEMgICU+JSBmaWx0ZXIodG90YWxNQkNvbnRyaWJ1dGlvbiA+IDAuMDUpClN0cm9uZ01CT04yQ1hfVGFyZ2V0c1R5cGUgPC0gdW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8pKQpTdHJvbmdNQk9OMkNYX1RhcmdldE5ldXJvbnMgPC0gZ2V0VHlwZXNUYWJsZShTdHJvbmdNQk9OMkNYX1RhcmdldHNUeXBlKQojIGhlYWQoU3Ryb25nTUJPTjJDWF9UYXJnZXROZXVyb25zKQoKIyBGaW5kIHRoZSBNQk9OIGJvZHlpZHMKU3Ryb25nTUJPTjJDWF9Gcm9tVHlwZSA8LSB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS5mcm9tKSkKU3Ryb25nTUJPTjJDWF9Gcm9tTmV1cm9ucyA8LSBnZXRUeXBlc1RhYmxlKFN0cm9uZ01CT04yQ1hfRnJvbVR5cGUpCiMgaGVhZChTdHJvbmdNQk9OMkNYX0Zyb21OZXVyb25zKQoKIyBGaW5kIG5ldXJvbiB0byBuZXVyb24gY29ubmVjdGlvbiB0YWJsZSBmb3Igc3Ryb25nIGRpcmVjdCBNQk9OIHRvIENYIGNvbm5lY3Rpb25zClN0cm9uZ01CT04yQ1hfQ29ublRhYmxlIDwtIGdldENvbm5lY3Rpb25UYWJsZV9mb3JTdWJzZXQoU3Ryb25nTUJPTjJDWF9Gcm9tTmV1cm9ucyRib2R5aWQsIFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucyRib2R5aWQpCgojIEFycmFuZ2UgU3Ryb25nTUJPTjJDWF9Db25uVGFibGUgaW4gdGhlIG9yZGVyIG9mIFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50bwpTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSR0eXBlLnRvIDwtIGZhY3RvcihTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSR0eXBlLnRvLGxldmVscz11bmlxdWUoU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvKSxvcmRlcmVkPVRSVUUpClN0cm9uZ01CT04yQ1hfQ29ublRhYmxlIDwtIGFycmFuZ2UoU3Ryb25nTUJPTjJDWF9Db25uVGFibGUsdHlwZS50bykKCiMgUGxvdCBTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZQpwbG90U3Ryb25nTUJPTjJDWF9Db25uVGFibGUgPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSxieUdyb3VwPSJpZCIpCnByaW50KHBsb3RTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSkKZ2dzYXZlKCJwbG90U3Ryb25nTUJPTjJDWF9Db25uVGFibGUuZXBzIiwgcGxvdD1wbG90U3Ryb25nTUJPTjJDWF9Db25uVGFibGUsIGRldmljZT0iZXBzIiwgcGF0aD1QbG90RGlyLCBzY2FsZT0xLCAKICAgICAgIHdpZHRoPTExLCBoZWlnaHQ9OCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPVRSVUUpCmBgYAoKIyMjIFBsb3QgdGhlIHN0cm9uZyBkaXJlY3QgcGF0aHdheXMgZnJvbSBNQk9OcyB0byBDWApgYGB7cn0KIyBTZXQgdXAgdGhlIGxheW91dCBmb3IgdGhlIHBhdGh3YXkgcGxvdAp0eXBlcyA8LSB1bmlxdWUoYyh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05UYXJnZXRJbnBDJHR5cGUuZnJvbSkpLHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTlRhcmdldElucEMkdHlwZS50bykpKSkKbnVtRnJvbSA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OVGFyZ2V0SW5wQyR0eXBlLmZyb20pKSkKbnVtVG8gPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTlRhcmdldElucEMkdHlwZS50bykpKQp4eUxvb2t1cCA9IGRhdGEuZnJhbWUodHlwZSA9IHR5cGVzLCB4ID0gYyhyZXAoLTEsdGltZXMgPSBudW1Gcm9tKSxyZXAoMCx0aW1lcyA9IG51bVRvKSksIHkgPSBjKHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1Gcm9tKSwgc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bVRvKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKU3Ryb25nTUJPTlRhcmdldElucENkZiA8LSBTdHJvbmdNQk9OVGFyZ2V0SW5wQwpTdHJvbmdNQk9OVGFyZ2V0SW5wQ2RmJHR5cGUudG8gPC0gYXMudmVjdG9yKFN0cm9uZ01CT05UYXJnZXRJbnBDZGYkdHlwZS50bykKZ2dfTUJPTjJUYXJnZXRUYWIgPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT05UYXJnZXRJbnBDZGYseHlMb29rdXAsRkFMU0UsVFJVRSkKIyBnZ19NQk9OMlRhcmdldFRhYiA8LSBncmFwaENvblRhYlBvbHlDaHJvbWUoU3Ryb25nTUJPTlRhcmdldElucENkZix4eUxvb2t1cCxGQUxTRSxUUlVFKQpnZ19NQk9OMlRhcmdldFRhYiA8LSBnZ19NQk9OMlRhcmdldFRhYiArIHNjYWxlX3lfcmV2ZXJzZSgpCnByaW50KGdnX01CT04yVGFyZ2V0VGFiKQpnZ3NhdmUoIlN0cm9uZ01CT04yQ1h0YXJnZXRzVGFiLnN2ZyIsIHBsb3Q9Z2dfTUJPTjJUYXJnZXRUYWIsIGRldmljZT0ic3ZnIiwgcGF0aD1QbG90RGlyLCBzY2FsZT0xLCAKICAgICAgIHdpZHRoPTExLCBoZWlnaHQ9OCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPVRSVUUpCmBgYApOb3RlczogCjEpIE1CT04wNSBoYXMgbm8gYnJhbmNoZXMgaXBzaWxhdGVyYWwgdG8gdGhlIGNlbGwgYm9keSwgb25seSBjb250cmFsYXRlcmFsLiBGQjRSIG9ubHkgaGFzIGRlbmRyaXRlcyBpcHNpbGF0ZXJhbCB0byBjZWxsIGJvZHkuIFRoZXJlZm9yZSwgb25seSBNQk9OMDVfTCAobm90IE1CT04wNV9SKSBtYWtlcyBzeW5hcHNlcyBvbnRvIEZCNFJfUi4KMikgTUJPTjA5IGhhcyBiaWxhdGVyYWwgYnJhbmNoZXMsIGJ1dCBvbmx5IG1ha2VzIGNvbnRhY3Qgd2l0aCBpcHNpbGF0ZXJhbCBGQjRSLCBpLmUuLCBNQk9OMDlfUiBvbnRvIEZCNFJfUi4KMykgTUJPTjIxIGhhcyBiaWxhdGVyYWwgYnJhbmNoZXMsIGFuZCBzeW5hcHNlcyBvbnRvIEZCNFIgYmlsYXRlcmFsbHkuCjQpIE1CT04wNCBoYXMgYmlsYXRlcmFsIGJyYW5jaGVzLCBidXQgbW9yZSBjb250cmFsYXRlcmFsbHkgcmVsYXRpdmUgdG8gdGhlIG5hbWUgKGFsdGhvdWdoIHRoZSAqX0wgY2VsbCBib2R5IGlzIGp1c3Qgb2ZmIHRvIHRoZSBtaWRsaW5lIG9uIHRoZSByaWdodCBzaWRlKS4KNSkgTUJPTjEyUiBhbmQgTUJPTjEzUiBvbmx5IGhhdmUgYnJhbmNoZXMgaXBzaWxhdGVyYWwgdG8gdGhlIGNlbGwgYm9keS4KNikgTUJPTjAzIGhhcyBiaWxhdGVyYWwgYnJhbmNoZXMsIGJ1dCBtb3JlIGNvbnRyYWxhdGVyYWxseSByZWxhdGl2ZSB0byB0aGUgbmFtZS4KNykgTUJPTjIyIGhhcyBpcHNpbGF0ZXJhbCBicmFuY2hlcyBpbiB0aGUgY2FseXggYW5kIGJpbGF0ZXJhbCBicmFuY2hlcyBpbiBTSVAvU01QLgo4KSBNQk9OMzAgaGFzIGJpbGF0ZXJhbCBicmFuY2hlcywgYnV0IG1vcmUgaXBzaWxhdGVyYWwgdG8gdGhlIGNlbGwgYm9keS4KCiMjIyBQbG90IHRoZSBjb21iaW5lZCBkaXJlY3QgYW5kIGluZGlyZWN0IHBhdGh3YXlzIGZyb20gTUJPTnMgdG8gQ1hfUgpgYGB7cn0KIyBDb21iaW5lIE1CT04gdG8gQ1hfUiBkaXJlY3QgYW5kIGluZGlyZWN0IHRhYmxlcyAKU3Ryb25nTUJPTjJDWFJUZ3RUYWJsZSA8LSBTdHJvbmdNQk9OVGFyZ2V0SW5wQ2RmICU+JSBzZWxlY3QodHlwZS5mcm9tLHR5cGUudG8sd2VpZ2h0UmVsYXRpdmUpClN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvVGFibGUgPC0gYmluZF9yb3dzKFN0cm9uZ01CT04yQ1hSVGd0VGFibGUsU3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9UYWJsZSwgLmlkID0gTlVMTCkKCiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBjb21iaW5lZCBwYXRod2F5IHBsb3QKTUJPTnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFJUZ3RUYWJsZSR0eXBlLmZyb20pKSx1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUkdHlwZS5mcm9tKSkpKQpudW1NQk9OcyA8LSBsZW5ndGgoTUJPTnR5cGVzKQptaWROb2RlcyA8LSB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUkdHlwZS5mcm9tKSkKbnVtTWlkTm9kZXMgPC0gbGVuZ3RoKG1pZE5vZGVzKQphbGxDWFJ0YXJnZXRzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFJUZ3RUYWJsZSR0eXBlLnRvKSksdW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUlRhYmxlJHR5cGUudG8pKSkpCm51bUNYdGFyZ2V0cyA8LSBsZW5ndGgoYWxsQ1hSdGFyZ2V0cykKdHlwZXMgPC0gdW5pcXVlKGMoTUJPTnR5cGVzLG1pZE5vZGVzLGFsbENYUnRhcmdldHMpKQpudW1UeXBlcyA8LSBsZW5ndGgodHlwZXMpCgp4eUxvb2t1cCA9IGRhdGEuZnJhbWUodHlwZSA9IHR5cGVzLCB4ID0gYyhyZXAoLTEsdGltZXMgPSBudW1NQk9OcyksIHJlcCgwLHRpbWVzID0gbnVtTWlkTm9kZXMpLCByZXAoMSx0aW1lcyA9IG51bUNYdGFyZ2V0cykpLCAKICAgICAgICAgICAgICAgICAgICAgIHkgPSBjKHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1NQk9OcyksIHNlcSgxLjEsMy4xLGxlbmd0aC5vdXQgPSBudW1NaWROb2RlcyksIHNlcSgtMSwxLjUsbGVuZ3RoLm91dCA9IG51bUNYdGFyZ2V0cykpKQoKIyBHcmFwaCB0aGUgVHlwZVRvVHlwZSBDb25uVGFibGUgdXNpbmcgdGhlIGxvb2t1cFRhYmxlClN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvUGF0aCA8LSBncmFwaENvblRhYl9vbGQoU3Ryb25nTUJPTjJDWFJUZ3REcmN0SW5kcmN0Q29tYm9UYWJsZSx4eUxvb2t1cCxGQUxTRSxUUlVFKQpTdHJvbmdNQk9OMkNYUlRndERyY3RJbmRyY3RDb21ib1BhdGggPC0gU3Ryb25nTUJPTjJDWFJUZ3REcmN0SW5kcmN0Q29tYm9QYXRoICsgc2NhbGVfeV9yZXZlcnNlKCkKcHJpbnQoU3Ryb25nTUJPTjJDWFJUZ3REcmN0SW5kcmN0Q29tYm9QYXRoKQpnZ3NhdmUoIlN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvUGF0aC5zdmciLCBwbG90PVN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvUGF0aCwgZGV2aWNlPSJzdmciLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MzAsIGhlaWdodD05MCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQoKYGBgCgoKCiMjIyBGaW5kIGNvbm5lY3Rpb25zIGZyb20gc3Ryb25nIGRpcmVjdCBNQk9OIHRhcmdldHMgdG8gc2Vjb25kYXJ5IHRhcmdldHMgaW4gQ1gKYGBge3J9CiMgRmluZCB0aGUgcG9zdHN5bmFwdGljIHBhcnRuZXJzIG9mIFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucwpTdHJvbmdNQk9OMkNYX1RhcmdldHNDbmN0VGFibGUgPC0gZ2V0Q29ubmVjdGlvblRhYmxlKFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucyxzeW5hcHNlVHlwZT0iUE9TVCIpCiMgaGVhZChTdHJvbmdNQk9OMkNYX1RhcmdldHNDbmN0VGFibGUpClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiA8LSBnZXRDb25uZWN0aW9uVGFibGVfZm9yU3Vic2V0KFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucyRib2R5aWQsIFN0cm9uZ01CT04yQ1hfVGFyZ2V0c0NuY3RUYWJsZSR0bywiRkIiKQoKIyBBcnJhbmdlIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiBpbiB0aGUgb3JkZXIgb2YgU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiR0eXBlLmZyb20gPC0gZmFjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiR0eXBlLmZyb20sbGV2ZWxzPXVuaXF1ZShTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8pLG9yZGVyZWQ9VFJVRSkKU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCIDwtIGFycmFuZ2UoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCLHR5cGUuZnJvbSkKCiMgUGxvdCBhbmQgc2F2ZSB0aGUgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCIGJhc2VkIG9uIHRoZSBjb25uZWN0aW9uTWVhc3VyZSBvZiAid2VpZ2h0UmVsYXRpdmUiCnBsb3RTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIgPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIsYnlHcm91cD0iaWQiKQpwcmludChwbG90U3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCKQpnZ3NhdmUoInBsb3RTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIuZXBzIiwgcGxvdD1wbG90U3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD04MCwgaGVpZ2h0PTEwLCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCgojIEdldCB0eXBlIHRvIHR5cGUgY29ubmVjdGlvbiB0YWJsZQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIkdHlwZS5mcm9tIDwtIGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIkdHlwZS5mcm9tKQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIGdldFR5cGVUb1R5cGVUYWJsZShTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIpCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiICU+JSBhcnJhbmdlKHR5cGUuZnJvbSAlaW4lIFN0cm9uZ01CT04yQ1hfVGFyZ2V0c1R5cGUpCgojIEZpbHRlciB0aGUgdGFibGUgYnkgd2VpZ2h0UmVsYXRpdmUKU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiICAlPiUgZmlsdGVyKHdlaWdodFJlbGF0aXZlID4gMC4wMSkKIyBFeGNsdWRlIGNvbm5lY3Rpb25zIHRvIHNlbGYgdHlwZXMKU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiICU+JSBmaWx0ZXIodHlwZS5mcm9tICE9IHR5cGUudG8pCgojIEFycmFuZ2UgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiBpbiB0aGUgb3JkZXIgb2YgU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tIDwtIGZhY3RvcihTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSxsZXZlbHM9dW5pcXVlKFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50byksb3JkZXJlZD1UUlVFKQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIGFycmFuZ2UoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYix0eXBlLmZyb20pCgojIFBsb3QgYW5kIHNhdmUgdGhlIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgYmFzZWQgb24gdGhlIGNvbm5lY3Rpb25NZWFzdXJlIG9mICJ3ZWlnaHRSZWxhdGl2ZSIKcGxvdFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVDb25uIDwtIHBsb3RDb25uZWN0aXZpdHlNYXRyaXgoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYixieUdyb3VwPSJ0eXBlIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQpwcmludChwbG90U3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZUNvbm4pCmdnc2F2ZSgiU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYmxlX3dlaWdodFJlbGF0aXZlLmVwcyIsIHBsb3Q9cGxvdFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVDb25uLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xMSwgaGVpZ2h0PTgsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1UUlVFKQpgYGAKCiMjIyBQbG90IHRoZSBwYXRod2F5cyBmcm9tIHN0cm9uZyBkaXJlY3QgTUJPTi0+Q1ggdGFyZ2V0cyB0byBzZWNvbmRhcnkgQ1ggdGFyZ2V0cwpgYGB7cn0KIyBTZXQgdXAgdGhlIGxheW91dCBmb3IgdGhlIHBhdGh3YXkgcGxvdAp0eXBlcyA8LSB1bmlxdWUoYyh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSksc29ydCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS50bykpKSAgKSAgICkKbnVtVHlwZXMgPC0gbGVuZ3RoKHR5cGVzKQpudW1Gcm9tIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSkpCm51bVRvIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS50bykpKQoKIyBBcnJhbmdlIHRoZSBwcmltYXJ5IE1CT04tPkNYIHRhcmdldHMgaW4gYSBjaXJjbGUKYW5nc0Zyb20gPC0gc2VxKC1waSxwaSxsZW5ndGgub3V0ID0gbnVtRnJvbSArIDEpCmFuZ3NGcm9tIDwtIGFuZ3NGcm9tWzE6KGxlbmd0aChhbmdzRnJvbSktMSldCnhGcm9tIDwtIDEqc2luKGFuZ3NGcm9tKQp5RnJvbSA8LSAxKmNvcyhhbmdzRnJvbSkKCiMgQXJyYW5nZSB0aGUgc2Vjb25kYXJ5IE1CT04tPkNYIHRhcmdldHMgaW4gYSBiaWdnZXIgY2lyY2xlCmFuZ3NUbyA8LSBzZXEoLXBpLHBpLGxlbmd0aC5vdXQgPSBudW1UeXBlcyAtIG51bUZyb20gKyAxKQphbmdzVG8gPC0gYW5nc1RvWzE6KGxlbmd0aChhbmdzVG8pLTEpXQp4VG8gPC0zKnNpbihhbmdzVG8pCnlUbyA8LTMqY29zKGFuZ3NUbykKCiMgeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtRnJvbSkscmVwKDAsdGltZXMgPSBudW1UeXBlcy1udW1Gcm9tKSksIAojICAgICAgICAgICAgICAgICAgICAgIHkgPSBjKHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1Gcm9tKSwgc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bVR5cGVzLW51bUZyb20pKSkKCiMgeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMoeEZyb20scmVwKDMsdGltZXMgPSBudW1UeXBlcy1udW1Gcm9tKSksIAojICAgICAgICAgICAgICAgICAgICAgIHkgPSBjKHlGcm9tLCBzZXEoLTgsOCxsZW5ndGgub3V0ID0gbnVtVHlwZXMtbnVtRnJvbSkpKQoKeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMoeEZyb20seFRvKSwgeSA9IGMoeUZyb20seVRvKSkKCiMgR3JhcGggdGhlIFR5cGVUb1R5cGUgQ29ublRhYmxlIHVzaW5nIHRoZSB4eUxvb2t1cCBsb29rdXBUYWJsZQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSA8LSBhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pCmdnX01CT04xc3RUbzJuZGFyeVRhcmdldFRhYiA8LSBncmFwaENvblRhYl9vbGQoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYix4eUxvb2t1cCxGQUxTRSxUUlVFKQpnZ19NQk9OMXN0VG8ybmRhcnlUYXJnZXRUYWIgPC0gZ2dfTUJPTjFzdFRvMm5kYXJ5VGFyZ2V0VGFiICsgc2NhbGVfeV9yZXZlcnNlKCkKcHJpbnQoZ2dfTUJPTjFzdFRvMm5kYXJ5VGFyZ2V0VGFiKQpnZ3NhdmUoIk1CT04xc3RUbzJuZGFyeVRhcmdldFRhYi5zdmciLCBwbG90PWdnX01CT04xc3RUbzJuZGFyeVRhcmdldFRhYiwgZGV2aWNlPSJzdmciLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTYsIGhlaWdodD0xNiwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQpgYGAKCiMjIyBDbHVzdGVyIE1CLT5DWCBwcmltYXJ5IHRhcmdldHMgYnkgdGhlIGNvcnJlbGF0aW9uIG9mIHRoZWlyIHdlaWdodFJlbGF0aXZlIGNvbm5lY3Rpdml0eSB0byBzZWNvbmRhcnkgdGFyZ2V0cyBhbmQgdGhlIGludmVyc2UgKGNsdXN0ZXJpbmcgc2Vjb25kYXJ5IHRhcmdldHMgYmFzZWQgb24gdGhlaXIgaW5wdXRzKQpgYGB7cn0KU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZV9jbHVzdGVyZWQgPC0gcGxvdENvcnJNYXRDbHVzdGVyKFBsb3REaXIsU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiwnU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZScpClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeUZyb20gPC0gU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZV9jbHVzdGVyZWRbWzFdXQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUbyA8LSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlX2NsdXN0ZXJlZFtbMl1dCnJtKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVfY2x1c3RlcmVkKTsKYGBgCgoKYGBge3J9CiMgUGxvdCB0aGUgY29ubmVjdGlvbnMgYnkgcGF0aHdheVdlaWdodCAoPSB0b3RhbE1CQ29udHJpYnV0aW9uICogd2VpZ2h0UmVsYXRpdmUpCgpgYGAKCiMjIyBGaW5kIHRlcnRpYXJ5IE1CT04gdGFyZ2V0cyBpbiB0aGUgQ1gKYGBge3J9CiMgRmluZCB0aGUgcG9zdHN5bmFwdGljIHBhcnRuZXJzIG9mIHNlY29uZGFyeSBTdHJvbmdNQk9OMkNYX1RhcmdldE5ldXJvbnMKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9UeXBlcyA8LSB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkZGF0YWJhc2VUeXBlLnRvKSkKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9OZXVyb25zIDwtIGdldFR5cGVzVGFibGUoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9UeXBlcykKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzQ25jdFRhYmxlIDwtIGdldENvbm5lY3Rpb25UYWJsZShTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNJbkZCX05ldXJvbnMkYm9keWlkLHN5bmFwc2VUeXBlPSJQT1NUIikKIyBoZWFkKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c0NuY3RUYWJsZSkKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQiA8LSBnZXRDb25uZWN0aW9uVGFibGVfZm9yU3Vic2V0KFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c0luRkJfTmV1cm9ucyRib2R5aWQsIFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c0NuY3RUYWJsZSR0bywiRkIiKQoKIyBBcnJhbmdlIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiBpbiB0aGUgb3JkZXIgb2YgU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCJHR5cGUuZnJvbSA8LSBmYWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCJHR5cGUuZnJvbSxsZXZlbHM9dW5pcXVlKFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50byksb3JkZXJlZD1UUlVFKQojIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiA8LSBhcnJhbmdlKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQix0eXBlLmZyb20pCgojIFBsb3QgYW5kIHNhdmUgdGhlIFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkIgYmFzZWQgb24gdGhlIGNvbm5lY3Rpb25NZWFzdXJlIG9mICJ3ZWlnaHRSZWxhdGl2ZSIKcGxvdFN0cm9uZ01CT04yQ1gybmRUYXJnZXRzVG9Qb3N0SW5GQiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkIsYnlHcm91cD0iaWQiKQpwcmludChwbG90U3Ryb25nTUJPTjJDWDJuZFRhcmdldHNUb1Bvc3RJbkZCKQpnZ3NhdmUoInBsb3RTdHJvbmdNQk9OMkNYMm5kVGFyZ2V0c1RvUG9zdEluRkIuZXBzIiwgcGxvdD1wbG90U3Ryb25nTUJPTjJDWDJuZFRhcmdldHNUb1Bvc3RJbkZCLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xODAsIGhlaWdodD04MCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQoKIyBHZXQgdHlwZSB0byB0eXBlIGNvbm5lY3Rpb24gdGFibGUKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgPC0gZ2V0VHlwZVRvVHlwZVRhYmxlKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkIpCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiICU+JSBhcnJhbmdlKHR5cGUuZnJvbSAlaW4lIFN0cm9uZ01CT04yQ1hfVGFyZ2V0c1R5cGUpCgojIEZpbHRlciB0aGUgdGFibGUgYnkgd2VpZ2h0UmVsYXRpdmUKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgPC0gU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgICU+JSBmaWx0ZXIod2VpZ2h0UmVsYXRpdmUgPiAwLjAxKQojIEV4Y2x1ZGUgY29ubmVjdGlvbnMgdG8gc2VsZiB0eXBlcwpTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiAlPiUgZmlsdGVyKHR5cGUuZnJvbSAhPSB0eXBlLnRvKQoKIyBBcnJhbmdlIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgaW4gdGhlIG9yZGVyIG9mIFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50bwojIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tIDwtIGZhY3RvcihTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSxsZXZlbHM9dW5pcXVlKFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50byksb3JkZXJlZD1UUlVFKQojIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgPC0gYXJyYW5nZShTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLHR5cGUuZnJvbSkKCiMgUGxvdCBhbmQgc2F2ZSB0aGUgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgYmFzZWQgb24gdGhlIGNvbm5lY3Rpb25NZWFzdXJlIG9mICJ3ZWlnaHRSZWxhdGl2ZSIKcGxvdFN0cm9uZ01CT04yQ1gybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGUgPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYixieUdyb3VwPSJ0eXBlIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQpwcmludChwbG90U3Ryb25nTUJPTjJDWDJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZSkKZ2dzYXZlKCJTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYmxlX3dlaWdodFJlbGF0aXZlLmVwcyIsIHBsb3Q9cGxvdFN0cm9uZ01CT04yQ1gybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGUsIGRldmljZT0iZXBzIiwgcGF0aD1QbG90RGlyLCBzY2FsZT0xLCAKICAgICAgIHdpZHRoPTI0LCBoZWlnaHQ9MTYsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1GQUxTRSkKYGBgCgojIyMgUGxvdCB0aGUgcGF0aHdheXMgZnJvbSBzdHJvbmcgZGlyZWN0IE1CT04tPkNYIHNlY29uZGFyeSBDWCB0YXJnZXRzIHRvIHRlcnRpYXJ5IENYIHRhcmdldHMKYGBge3J9CiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBwYXRod2F5IHBsb3QKdHlwZXMgPC0gdW5pcXVlKGModW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSxzb3J0KHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS50bykpKSAgKSAgICkKbnVtVHlwZXMgPC0gbGVuZ3RoKHR5cGVzKQpudW1Gcm9tIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSkpKQpudW1UbyA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLnRvKSkpCgojIEFycmFuZ2UgdGhlIHNlY29uZGFyeSBNQk9OLT5DWCB0YXJnZXRzIGluIGEgY2lyY2xlCmFuZ3NGcm9tIDwtIHNlcSgtcGkscGksbGVuZ3RoLm91dCA9IG51bUZyb20gKyAxKQphbmdzRnJvbSA8LSBhbmdzRnJvbVsxOihsZW5ndGgoYW5nc0Zyb20pLTEpXQp4RnJvbSA8LSA0KnNpbihhbmdzRnJvbSkKeUZyb20gPC0gNCpjb3MoYW5nc0Zyb20pCgojIEFycmFuZ2UgdGhlIHRlcnRpYXJ5IE1CT04tPkNYIHRhcmdldHMgaW4gYSBiaWdnZXIgY2lyY2xlCmFuZ3NUbyA8LSBzZXEoLXBpLHBpLGxlbmd0aC5vdXQgPSBudW1UeXBlcyAtIG51bUZyb20gKyAxKQphbmdzVG8gPC0gYW5nc1RvWzE6KGxlbmd0aChhbmdzVG8pLTEpXQp4VG8gPC01KnNpbihhbmdzVG8pCnlUbyA8LTUqY29zKGFuZ3NUbykKCiMgeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtRnJvbSkscmVwKDAsdGltZXMgPSBudW1UeXBlcy1udW1Gcm9tKSksIAojICAgICAgICAgICAgICAgICAgICAgIHkgPSBjKHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1Gcm9tKSwgc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bVR5cGVzLW51bUZyb20pKSkKCiMgeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMoeEZyb20scmVwKDMsdGltZXMgPSBudW1UeXBlcy1udW1Gcm9tKSksIAojICAgICAgICAgICAgICAgICAgICAgIHkgPSBjKHlGcm9tLCBzZXEoLTgsOCxsZW5ndGgub3V0ID0gbnVtVHlwZXMtbnVtRnJvbSkpKQoKeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMoeEZyb20seFRvKSwgeSA9IGMoeUZyb20seVRvKSkKCiMgR3JhcGggdGhlIFR5cGVUb1R5cGUgQ29ublRhYmxlIHVzaW5nIHRoZSB4eUxvb2t1cCBsb29rdXBUYWJsZQpnZ19NQk9OMm5kVG8zcmRUYXJnZXRUYWIgPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLHh5TG9va3VwLEZBTFNFLFRSVUUpCmdnX01CT04ybmRUbzNyZFRhcmdldFRhYiA8LSBnZ19NQk9OMm5kVG8zcmRUYXJnZXRUYWIgKyBzY2FsZV95X3JldmVyc2UoKQpwcmludChnZ19NQk9OMm5kVG8zcmRUYXJnZXRUYWIpCmdnc2F2ZSgiTUJPTjJuZFRvM3JkQ1hUYXJnZXRUYWIuc3ZnIiwgcGxvdD1nZ19NQk9OMm5kVG8zcmRUYXJnZXRUYWIsIGRldmljZT0ic3ZnIiwgcGF0aD1QbG90RGlyLCBzY2FsZT0xLCAKICAgICAgIHdpZHRoPTMwLCBoZWlnaHQ9MzAsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1GQUxTRSkKYGBgCgojIyMgQ2x1c3RlciBNQi0+Q1ggc2Vjb25kYXJ5IHRhcmdldHMgYnkgdGhlIGNvcnJlbGF0aW9uIG9mIHRoZWlyIHdlaWdodFJlbGF0aXZlIGNvbm5lY3Rpdml0eSB0byB0ZXJ0aWFyeSB0YXJnZXRzIGFuZCB0aGUgaW52ZXJzZSAoY2x1c3RlcmluZyB0ZXJ0aWFyeSB0YXJnZXRzIGJhc2VkIG9uIHRoZWlyIGlucHV0cykKYGBge3J9ClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlX2NsdXN0ZXJlZCA8LSBwbG90Q29yck1hdENsdXN0ZXIoUGxvdERpcixTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiwnU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGUnKQpTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG8gPC0gU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVfY2x1c3RlcmVkW1syXV0KYGBgCgojIyMgUGF0aHdheSBwbG90IGJhc2VkIG9uIHRoZSBjbHVzdGVyZWQgYW5kIHJlYXJyYW5nZWQgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvCmBgYHtyfQojIFNldCB1cCB0aGUgbGF5b3V0IGZvciB0aGUgcGF0aHdheSBwbG90CnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvJHR5cGUuZnJvbSkpLHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvJHR5cGUudG8pKSkpCm51bVR5cGVzIDwtIGxlbmd0aCh0eXBlcykKbnVtRnJvbSA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG8kdHlwZS5mcm9tKSkpCm51bVRvIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUbyR0eXBlLnRvKSkpCnh5TG9va3VwID0gZGF0YS5mcmFtZSh0eXBlID0gdHlwZXMsIHggPSBjKHJlcCgtMSx0aW1lcyA9IG51bUZyb20pLHJlcCgwLHRpbWVzID0gbnVtVHlwZXMtbnVtRnJvbSkpLCB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtRnJvbSksIHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1UeXBlcy1udW1Gcm9tKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvX2NvcHkgPC0gU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19jb3B5JHR5cGUudG8gPC0gYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19jb3B5JHR5cGUudG8pClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19wYXRod2F5IDwtIGdyYXBoQ29uVGFiX29sZChTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fY29weSx4eUxvb2t1cCxGQUxTRSxUUlVFKQojIGdnX01CT04yVGFyZ2V0VGFiIDwtIGdyYXBoQ29uVGFiUG9seUNocm9tZShTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSx4eUxvb2t1cCxGQUxTRSxUUlVFKQpTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSA8LSBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSArIHNjYWxlX3lfcmV2ZXJzZSgpCnByaW50KFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19wYXRod2F5KQpnZ3NhdmUoIlN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2NsdXN0ZXJCeVRvX3BhdGh3YXkuc3ZnIiwgcGxvdD1TdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSwgZGV2aWNlPSJzdmciLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MzAsIGhlaWdodD02MCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQoKYGBgCgojIyMjIyMjIyMjIyByYW4gd2VsbCB1cCB0byBoZXJlICMjIyMjIyMjIyMjIyMjIyMKCiMjIyBwdXQgYSBicmVhayBoZXJlCgpgYGB7cn0KIyBSdW4gdC1TTkUgb24gdGhlIHR5cGUudG8gb2YgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIKIyBsaWJyYXJ5KE0zQykKIyB0c25lKERhdGE0Q2x1c3QscGVycGxleD0xNSkKCmBgYAoKIyMjIENoZWNrIGluZGlyZWN0IGNvbm5lY3Rpb25zIGZyb20gTUJPTnMgdG8gdGhlIENYCmBgYHtyfQojIFBsb3QgbmV1cm9uIHRvIG5ldXJvbiBjb25uZWN0aW9uIG1hdHJpeCBmcm9tIE1CT05zIHRvIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzCk1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSA8LSBNQk9OX1Bvc3RDb25uZWN0aW9ucyAlPiUgZmlsdGVyKHR5cGUudG8gJWluJSBNQk9Obm9uQ1hUYXJnZXRzTCkKcGxvdE1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSxieUdyb3VwPSJpZCIsY29ubmVjdGlvbk1lYXN1cmU9IndlaWdodFJlbGF0aXZlIikKcHJpbnQocGxvdE1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSkKCiMgUGxvdCB0eXBlIHRvIHR5cGUgY29ubmVjdGlvbiBtYXRyaXggZnJvbSBNQk9OcyB0byBub24tQ1ggbm9uLU1CT04gdGFyZ2V0cwpNQk9OX25vbkNYbm9uTUJPTl9UeXBlMlR5cGVUYWIgPC0gZ2V0VHlwZVRvVHlwZVRhYmxlKE1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSkKIyBGaWx0ZXIgdGhlIHRhYmxlIGJ5IHdlaWdodFJlbGF0aXZlCk1CT05fbm9uQ1hub25NQk9OX1R5cGUyVHlwZVRhYiA8LSBNQk9OX25vbkNYbm9uTUJPTl9UeXBlMlR5cGVUYWIgICU+JSBmaWx0ZXIod2VpZ2h0UmVsYXRpdmUgPiAwLjAxKQoKcGxvdE1CT05fbm9uQ1hub25NQk9OX1R5cGUyVHlwZUNvbm4gPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChNQk9OX25vbkNYbm9uTUJPTl9UeXBlMlR5cGVUYWIsYnlHcm91cD0idHlwZSIsY29ubmVjdGlvbk1lYXN1cmU9IndlaWdodFJlbGF0aXZlIikKcHJpbnQocGxvdE1CT05fbm9uQ1hub25NQk9OX1R5cGUyVHlwZUNvbm4pCgojIFBsb3QgbmV1cm9uIHRvIG5ldXJvbiBjb25uZWN0aW9uIG1hdHJpeCBmcm9tIE1CT04gbm9uLUNYIG5vbi1NQk9OIHRhcmdldHMgdG8gdGhlaXIgdGFyZ2V0cyBpbiB0aGUgQ1gKTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfQ29ublRhYmxlIDwtIE1CT05fMm5kYXJ5UG9zdENvbm5lY3Rpb25zICU+JSBmaWx0ZXIodHlwZS50byAlaW4lIE1CT05DWDJuZGFyeVRhcmdldHNMKQpwbG90TUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfQ29ublRhYmxlIDwtIHBsb3RDb25uZWN0aXZpdHlNYXRyaXgoTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfQ29ublRhYmxlLGJ5R3JvdXA9ImlkIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQpwcmludChwbG90TUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfQ29ublRhYmxlKQoKIyBQbG90IHR5cGUgdG8gdHlwZSBjb25uZWN0aW9uIG1hdHJpeCBmcm9tIE1CT04gbm9uLUNYIG5vbi1NQk9OIHRhcmdldHMgdG8gdGhlaXIgdGFyZ2V0cyBpbiB0aGUgQ1gKTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiIDwtIGdldFR5cGVUb1R5cGVUYWJsZShNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9Db25uVGFibGUpCiMgRmlsdGVyIHRoZSB0YWJsZSBieSB3ZWlnaHRSZWxhdGl2ZQpNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIgPC0gTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiICAlPiUgZmlsdGVyKHdlaWdodFJlbGF0aXZlID4gMC4wMSkKIyBFeGNsdWRlIGNvbm5lY3Rpb25zIHRvIHNlbGYgdHlwZXMKTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiIDwtIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiAlPiUgZmlsdGVyKHR5cGUuZnJvbSAhPSB0eXBlLnRvKQoKcGxvdE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYixieUdyb3VwPSJ0eXBlIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQpwcmludChwbG90TUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiKQoKIyBGaWx0ZXIgTUJPTl9ub25DWG5vbk1CT05fVHlwZTJUeXBlVGFiIGJhc2VkIG9uIHR5cGUudG8gd2l0aCBkaXJlY3QgY29ubmVjdGlvbnMgaW4gdGhlIENYCk1CT05fbm9uQ1hub25NQk9Od0NYMm5kVF9UeXBlMlR5cGVUYWIgPC0gTUJPTl9ub25DWG5vbk1CT05fVHlwZTJUeXBlVGFiICU+JSBmaWx0ZXIodHlwZS50byAlaW4lIHVuaXF1ZShNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSkKTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiIDwtIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiAlPiUgZmlsdGVyKHR5cGUuZnJvbSAlaW4lIHVuaXF1ZShNQk9OX25vbkNYbm9uTUJPTndDWDJuZFRfVHlwZTJUeXBlVGFiJHR5cGUudG8pKQoKIyBQbG90IHBhdGh3YXlzIGZyb20gTUJPTnMgdG8gbm9uLUNYIG5vbi1NQk9OIHRhcmdldHMgdG8gc2Vjb25kYXJ5IENYIHRhcmdldHMKIyBDb21iaW5lIHRhYmxlcwpNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVDb21ib1RhYiA8LSBiaW5kX3Jvd3MoTUJPTl9ub25DWG5vbk1CT053Q1gybmRUX1R5cGUyVHlwZVRhYixNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIsIC5pZCA9IE5VTEwpCgojIFNldCB1cCB0aGUgbGF5b3V0IGZvciB0aGUgcGF0aHdheSBwbG90CnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT053Q1gybmRUX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSwgdW5pcXVlKGFzLnZlY3RvcihNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSksCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiJHR5cGUudG8pKSkpCm51bVR5cGVzIDwtIGxlbmd0aCh0eXBlcykKbnVtTUJPTnMgPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT053Q1gybmRUX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSkKbnVtTWlkTm9kZXMgPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSkpKQpudW1DWHRhcmdldHMgPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiJHR5cGUudG8pKSkKeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtTUJPTnMpLCByZXAoMCx0aW1lcyA9IG51bU1pZE5vZGVzKSwgcmVwKDEsdGltZXMgPSBudW1DWHRhcmdldHMpKSwgCiAgICAgICAgICAgICAgICAgICAgICB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtTUJPTnMpLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtTWlkTm9kZXMpLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtQ1h0YXJnZXRzKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlQ29tYm9QYXRoIDwtIGdyYXBoQ29uVGFiX29sZChNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVDb21ib1RhYix4eUxvb2t1cCxGQUxTRSxUUlVFKQpNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVDb21ib1BhdGggPC0gTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlQ29tYm9QYXRoICsgc2NhbGVfeV9yZXZlcnNlKCkKIyBwcmludChNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVDb21ib1BhdGgpCmdnc2F2ZSgiTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlQ29tYm9QYXRoLnN2ZyIsIHBsb3Q9TUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlQ29tYm9QYXRoLCBkZXZpY2U9InN2ZyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0zMCwgaGVpZ2h0PTQ1LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCgpgYGAKCiMjIyBDb21iaW5lIGRpcmVjdCBhbmQgaW5kaXJlY3QgTUJPTi0+Q1ggY29ubmVjdGlvbiB0YWJsZXMKYGBge3J9CgpgYGAKCgpgYGB7cn0KTUJPTjJuZGFyeUNYVGFyZ2V0VHlwZXMgPC0gdW5pcXVlKGFzLnZlY3RvcihNQk9OMm5kYXJ5VGFyZ2V0SW5wJGRhdGFiYXNlVHlwZS50bykpCgoKYGBgCgoKCgoKRm9jdXMgb24gdGhlIEZCIG5vdyAodGhlIG9ubHkgZXhjZXB0aW9uIGlzIExDTnAgd2hpY2ggY2FuIHByb2JhYmx5IGJlIHRyZWF0ZWQgc2VwYXJhdGVseSkgYW5kIHRyeSB0byBjb21wdXRlIGEgInBhdGh3YXkgd2VpZ2h0IiBwZXIgQ1ggdGFyZ2V0IHR5cGUuIEZvciBldmVyeSBjb25uZWN0aW9uLCBgTUJPTkluZmx1ZW5jZWAgaXMgdGhlIHByb2R1Y3Qgb2YgdGhlIHJlbGF0aXZlIHdlaWdodCB3aXRoIHRoZSB0b3RhbCBjb250cmlidXRpb24gb2YgTUJPTnMgdG8gdGhlIHByZXN5bmFwdGljIG5ldXJvbi4gYHBhdGh3YXlzV2VpZ2h0YCBpcyB0aGUgc3VtIG9mIHRoYXQgb3ZlciBhIHRhcmdldCB0eXBlIChiYXNpY2FsbHkgdGhlIHN1bSBvZiBhbGwgaXRzIGluZGlyZWN0IE1CT04gaW5mbHVlbmNlcykuIAoKKioqU2hvdWxkIGFsc28gZmluZCBwYXRod2F5cyB0aHJvdWdoIG1pZGRsZSBuZXVyb25zIG91dHNpZGUgb2YgdGhlIENYLiAKYGBge3J9Ck1CT05UYXJnZXRGQk91dCA8LSBNQk9OVGFyZ2V0QmFnTGF0JG91dHB1dHMgJT4lIGZpbHRlcihyb2k9PSJGQiIgJiB0eXBlLmZyb20gJWluJSBNQk9OVGFyZ2V0SW5wQyR0eXBlLnRvKSAlPiUgCiAgbXV0YXRlKE1CT05JbmZsdWVuY2UgPSBNQk9OVGFyZ2V0SW5wQyR0b3RhbE1CQ29udHJpYnV0aW9uW21hdGNoKHR5cGUuZnJvbSxNQk9OVGFyZ2V0SW5wQyR0eXBlLnRvKV0qd2VpZ2h0UmVsYXRpdmUpICU+JSBncm91cF9ieSh0eXBlLnRvKSAlPiUgbXV0YXRlKHBhdGh3YXlzV2VpZ2h0ID0gc3VtKE1CT05JbmZsdWVuY2UpKSAlPiUgdW5ncm91cCgpCiAgCmBgYApMb29raW5nIGF0IHRoaXMgdGFibGUgYW5kIHNvcnRpbmcgaXQgYWNjb3JkaW5nIHRvIHZhcmlvdXMgd2F5cywgdGhlIHRoaW5ncyB0aGF0IGZpcnN0IHBvcHMgb3V0IGlzIHRoYXQgaXQncyBtb3N0bHkgYSBGQiB0YW5nZW50aWFsIG5ldHdvcmsgKG1lYW5pbmcgbW9zdCBGQiB0YW5nZW50aWFsIG5ldXJvbiB0YXJnZXRzIGluIHRoZSBGQiBhcmUgb3RoZXIgRkIgdGFuZ2VudGlhbCBuZXVyb25zLCBJIGd1ZXNzIHNvbWV0aGluZyByZW1pbmlzY2VudCBvZiB0aGUgcmluZyBuZXVyb25zKS4gU28gd2UnZCBuZWVkIGEgd2F5IHRvIHdlYW4gdGhpcyByZWN1cnJlbmNlIGluIHRoZSB3YXkgd2UgY29uc2lkZXIgd2hpY2ggcGFydHMgb2YgdGhlIG5ldHdvcmsgYXJlIGluZmx1ZW5jZWQgYnkgdGhlIE1CT05zLgpgYGB7cn0KTUJPTlRhcmdldEZCT3V0U3VtbWFyeSA8LSBNQk9OVGFyZ2V0RkJPdXQgJT4lIGdyb3VwX2J5KHR5cGUudG8pICU+JSBzdW1tYXJpc2UocGF0aHdheXNXZWlnaHQgPSBwYXRod2F5c1dlaWdodFsxXSkKYGBgCgpDb25zaWRlcmluZyBGQjRSIHNlcGFyYXRlbHkKYGBge3J9Ck1CT05UYXJnZXRGQjRSIDwtIE1CT05UYXJnZXRGQk91dCAlPiUgZmlsdGVyKHR5cGUuZnJvbSA9PSAiRkI0Ul9SIikgJT4lIGdyb3VwX2J5KHR5cGUudG8pICU+JSBzdW1tYXJpc2UocGF0aHdheXNXZWlnaHQgPSBwYXRod2F5c1dlaWdodFsxXSkKYGBgCgpTdWdnZXN0aW9uOiByZWRvIHRoYXQsIGJ1dCBzdHVkeSB0aGUgcG9vbCBvZiBuZXVyb25zIGluZmx1ZW5jZWQgYnkgYSBzaW5nbGUgTUJPTiB0eXBlIGF0IGEgdGltZS4KCk90aGVyIGF4aXMgdG8gZXhwbG9yZTogRkIgdGFuZ2VudGlhbCBpbmhvbW9nZW5lb3VzIGlucHV0cyBvbnRvIGNvbHVtbmFyIG5ldXJvbnMuCgoK